釘釘客戶端JS-API權限簽名算法.NET版


  前段時間寫了一篇博文《釘釘如何進行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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM