当前位置: 首页 > news >正文

钉钉客户端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

 

相关文章:

  • Linux 卸载mysql-libs包出现错误
  • .net中的Queue和Stack
  • CSS中div覆盖另一个div
  • ARRAY_SIZE宏
  • 每日记录 2016-4-29 HTML5本地存储
  • Mina.Net实现的UDP多路广播
  • 向fedora添加rpmfusion源
  • Provisioning Services 7.8 入门系列教程之二 基础环境安装
  • iOS开发UI篇—UITableviewcell的性能优化和缓存机制
  • GlobalSign 增强型(EV) SSL 证书
  • 执行搜索 《第三篇》
  • Grovvy Step byStep Examples
  • oracle通过DBlink连接mysql(MariaDB)
  • Compile FreeCAD on Windows
  • Elasticsearch-DSL(highlight) 004
  • [原]深入对比数据科学工具箱:Python和R 非结构化数据的结构化
  • __proto__ 和 prototype的关系
  • HTML中设置input等文本框为不可操作
  • Java 内存分配及垃圾回收机制初探
  • js面向对象
  • mongo索引构建
  • php中curl和soap方式请求服务超时问题
  • 初识 webpack
  • 短视频宝贝=慢?阿里巴巴工程师这样秒开短视频
  • 聚类分析——Kmeans
  • 十年未变!安全,谁之责?(下)
  • 我从编程教室毕业
  • 学习HTTP相关知识笔记
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • 进程与线程(三)——进程/线程间通信
  • #QT(一种朴素的计算器实现方法)
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • #我与Java虚拟机的故事#连载17:我的Java技术水平有了一个本质的提升
  • (cljs/run-at (JSVM. :browser) 搭建刚好可用的开发环境!)
  • (TipsTricks)用客户端模板精简JavaScript代码
  • (附源码)springboot 智能停车场系统 毕业设计065415
  • (全注解开发)学习Spring-MVC的第三天
  • (学习日记)2024.01.19
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .NET CF命令行调试器MDbg入门(四) Attaching to Processes
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .net core MVC 通过 Filters 过滤器拦截请求及响应内容
  • .NET Core WebAPI中使用swagger版本控制,添加注释
  • .NET Remoting Basic(10)-创建不同宿主的客户端与服务器端
  • .net连接MySQL的方法
  • .sys文件乱码_python vscode输出乱码
  • ::什么意思
  • ??javascript里的变量问题
  • ?php echo ?,?php echo Hello world!;?
  • @SuppressWarnings注解
  • [ MSF使用实例 ] 利用永恒之蓝(MS17-010)漏洞导致windows靶机蓝屏并获取靶机权限
  • []error LNK2001: unresolved external symbol _m
  • [2016.7 test.5] T1
  • [ABP实战开源项目]---ABP实时服务-通知系统.发布模式
  • [AIGC] Kong:一个强大的 API 网关和服务平台