你的位置:首页 > ASP.net教程

[ASP.net教程]钉钉客户端JS


  前段时间写了一篇博文《钉钉如何进行PC端开发》,在里面并未解决本地生成签名的问题,需要到官网进行生成,由于钉钉门票等认证信息会超期,因此,必须能本地用代码自动更新相关参数信息,来换取签名。官方文档由于这块并未有.NET版本的签名API可供调用,无奈只能自己摸索着进行实现。可笑的是,在看钉钉文档时候并未理解其算法,但是却在看微信JS-API签名生成算法的时候顿悟了。感觉二者这个权限体系认证很是类似。

  话不多说,切入正题。

  钉钉广泛文档对于JS-API的说明是这样的:

开发者在web页面使用钉钉容器提供的jsapi时,需要验证调用权限,并以参数signature标识合法性

签名生成的规则:

List keyArray = sort(noncestr,timestamp,jsapi_ticket,url);

String str = assemble(keyArray);

signature = sha1(str);

参与签名的字段包括在上文中获取的jsapi_ticket,noncestr(随机字符串,自己随便填写即可),timestamp(当前时间戳,具体值为当前时间到1970年1月1号的秒数),url(当前网页的URL,不包含#及其后面部分)。例如:

  • noncestr=Zn4zmLFKD0wzilzM
  • jsapi_ticket=mS5k98fdkdgDKxkXGEs8LORVREiweeWETE40P37wkidkfksDSKDJFD5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcKIDU8l
  • timestamp=1414588745
  • url=http://open.dingtalk.com

步骤1. sort()含义为对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)

步骤2. assemble()含义为根据步骤1中获的参数字段的顺序,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串

步骤2. sha1()的含义为对在步骤2拼接好的字符串进行sha1加密。(PS:明明是步骤3好么!

  一开始没有理解,用代码生成签名和官方的就是对不上,很是打击。但是后来发现就是没有理解文档中的第一步,后来发现是:签名参数按照【字段名】而非一开始理解的参数值。

  既然理解了,那么先看看签名参数按照字段名noncestr、jsapi_ticket、timestamp和url的字典排序吧:

1 ArrayList AL = new ArrayList();2 AL.Add("noncestr");3 AL.Add("jsapi_ticket");4 AL.Add("timestamp");5 AL.Add("url");6 AL.Sort();

那么下一步就是拼接字符串了:

string assemble =string.Format("jsapi_ticket={0}&noncestr={1}&timestamp={2}&url={3}", jsapi_ticket, noncestr,sTimeStamp, url);

最后一步骤:

1 sha = new SHA1CryptoServiceProvider();2 enc = new ASCIIEncoding();3 byte[] dataToHash = enc.GetBytes(assemble);4 byte[] dataHashed = sha.ComputeHash(dataToHash);5 hash = BitConverter.ToString(dataHashed).Replace("-", "");6 hash = hash.ToLower();

完整代码如下,注意此包含时间戳的生产方法:

 1 using System; 2 using System.Text; 3 using System.Security.Cryptography; 4 using Suite.Corp; 5 using System.Collections; 6 /* 7 * Author:JackWangCUMT 8 * Date:2016-04-26 8:36 9 * Blogs:http://www.cnblogs.com/isaboy 10 * GitHub:https://github.com/JackWangCUMT 11 * QQ:308106637 12 */ 13 namespace myDDDev 14 { 15   public static class DingTalkAuth 16   { 17     /// <summary> 18     ///开发者在web页面使用钉钉容器提供的jsapi时,需要验证调用权限,并以参数signature标识合法性 19     ///签名生成的规则: 20     ///List keyArray = sort(noncestr, timestamp, jsapi_ticket, url); 21     /// String str = assemble(keyArray); 22     ///signature = sha1(str); 23     /// </summary> 24     /// <param name="noncestr">随机字符串,自己随便填写即可</param> 25     /// <param name="sTimeStamp">当前时间戳,具体值为当前时间到1970年1月1号的秒数</param> 26     /// <param name="jsapi_ticket">获取的jsapi_ticket</param> 27     /// <param name="url">当前网页的URL,不包含#及其后面部分</param> 28     /// <param name="signature">生成的签名</param> 29     /// <returns>0 成功,2 失败</returns> 30     public static int GenSigurate(string noncestr, string sTimeStamp, string jsapi_ticket, string url, ref string signature) 31     { 32  33  34       //例如: 35       //noncestr = Zn4zmLFKD0wzilzM 36       //jsapi_ticket = mS5k98fdkdgDKxkXGEs8LORVREiweeWETE40P37wkidkfksDSKDJFD5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcKIDU8l 37       //timestamp = 1414588745 38       //url = http://open.dingtalk.com 39  40       //步骤1.sort()含义为对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序) 41       //注意,此处是是按照【字段名】的ASCII字典序,而不是参数值的字典序(这个细节折磨我很久了) 42       //0:jsapi_ticket 1:noncestr 2:timestamp 3:url; 43  44       //步骤2.assemble()含义为根据步骤1中获的参数字段的顺序,使用URL键值对的格式(即key1 = value1 & key2 = value2…)拼接成字符串 45       //string assemble = "jsapi_ticket=3fOo5UfWhmvRKnRGMmm6cWwmIxDMCnniyVYL2fqcz1I4GNU4054IOlif0dZjDaXUScEjoOnJWOVrdwTCkYrwSl&noncestr=CUMT1987wlrrlw&timestamp=1461565921&url=https://jackwangcumt.github.io/home.html"; 46       string assemble =string.Format("jsapi_ticket={0}&noncestr={1}&timestamp={2}&url={3}", jsapi_ticket, noncestr,sTimeStamp, url); 47       //步骤2.sha1()的含义为对在步骤2拼接好的字符串进行sha1加密。 48       SHA1 sha; 49       ASCIIEncoding enc; 50       string hash = ""; 51       try 52       { 53         sha = new SHA1CryptoServiceProvider(); 54         enc = new ASCIIEncoding(); 55         byte[] dataToHash = enc.GetBytes(assemble); 56         byte[] dataHashed = sha.ComputeHash(dataToHash); 57         hash = BitConverter.ToString(dataHashed).Replace("-", ""); 58         hash = hash.ToLower(); 59       } 60       catch (Exception) 61       { 62         return 2; 63       } 64       signature = hash; 65       return 0; 66       67     } 68  69     /// <summary> 70     /// 获取时间戳timestamp(当前时间戳,具体值为当前时间到1970年1月1号的秒数) 71     /// </summary> 72     /// <returns></returns> 73     public static string GetTimeStamp() 74     { 75       TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); 76       return Convert.ToInt64(ts.TotalSeconds).ToString(); 77     } 78     /// <summary> 79     /// 字典排序 80     /// </summary> 81     public class DictionarySort : System.Collections.IComparer 82     { 83       public int Compare(object oLeft, object oRight) 84       { 85         string sLeft = oLeft as string; 86         string sRight = oRight as string; 87         int iLeftLength = sLeft.Length; 88         int iRightLength = sRight.Length; 89         int index = 0; 90         while (index < iLeftLength && index < iRightLength) 91         { 92           if (sLeft[index] < sRight[index]) 93             return -1; 94           else if (sLeft[index] > sRight[index]) 95             return 1; 96           else 97             index++; 98         } 99         return iLeftLength - iRightLength;100 101       }102     }103   }104 }

 代码已经放置在GitHub上:https://github.com/JackWangCUMT/DDHelper