请选择 进入手机版 | 继续访问电脑版

马上加入IBC程序猿 各种源码随意下,各种教程随便看! 注册 每日签到 加入编程讨论群

C#教程 ASP.NET教程 C#视频教程程序源码享受不尽 C#问题入口 ASP.NET问题入口

【C#问题提交】 社群合作 申请版主 程序开发 【远程协助】 每天乐一乐 每日签到 【承接毕业设计】 面试-葵花宝典下载

官方一群:

官方二群:

查看: 102|回复: 0

微信JSSDK签名

[复制链接]
  • TA的每日心情
    开心
    昨天 09:42
  • 签到天数: 1446 天

    [LV.10]以坛为家III

    926

    主题

    2828

    帖子

    9万

    积分

    管理员

    IBC编程社区-原道楠

    Rank: 9Rank: 9Rank: 9

    积分
    92121

    推广达人突出贡献优秀版主荣誉管理论坛元老

    发表于 2019-8-13 18:07:59 | 显示全部楼层 |阅读模式

    马上加入IBC,查看更多教程

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x

    微信JS-SDK说明文档

    https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115

    生成签名

    1.签名规则

    参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。

    对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。

    这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都接纳原始值,不进行URL 转义。

    2.注意事项

    1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。

    2.签名用的url必须是调用JS接口页面的完整URL。

    3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。

    4.调用接口时,请登录“微信公众平台-开发-基本配置”提前将服务器IP地址添加到IP白名单中,点击查看设置方法,否则将无法调用成功。小程序无需配置IP白名单。

    3.签名逻辑

    所知,签名字段有noncestr,jsapi_ticket,timestamp,url。那这四个值怎么来呢?

    noncestr:随机字符串,可以直接生成。

    1. string nonceStr=Guid.NewGuid().ToString("N");
    复制代码

    jsapi_ticket:公众号用于调用微信JS接口的临时单子(签名密钥)。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。

    获取jsapi_ticket时就要用到access_token了,access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。

    我们可以通过下面的接口取得access_token

    1. https请求方式: GET
    2. https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
    复制代码

    这里要用到3个参数(grant_type,appid,secret);

    参数 是否必须 说明
    grant_type 获取access_token填写client_credential
    appid 第三方用户唯一凭证
    secret 第三方用户唯一凭证密钥,即appsecret

    其中,grant_type的值即为client_credential,AppID和AppSecret可在“微信公众平台-开发-基本配置”页中获得(需要已经成为开发者,且帐号没有异常状态)。

    我在上篇随笔记录了AppID和AppSecret的获取方式,链接如下:

    https://www.cnblogs.com/p1024q/p/11321864.html

    正常情况下,微信会返回如下JSON数据

    1. {"access_token":"ACCESS_TOKEN","expires_in":7200}
    复制代码

    其中, access_token的值就是我们要用的值,expires_in 是凭证有效时间(7200秒)

    取到access_token后,要将值存到缓存不大于7200秒,再调用下面的接口

    1. http请求方式: GET
    2. https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
    复制代码

    成功则返回如下JSON

    1. {
    2. "errcode":0,
    3. "errmsg":"ok",
    4. "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
    5. "expires_in":7200
    6. }
    复制代码

    ticket值就是要用的jsapi_ticket。

    timestamp:时间戳.。

    url:当前网页的URL,不包含#及其后面部分。作为接口请求参数通过前端传过来。

    有了这四个值,就能根据签名规则进行签名,得到签名值signature。

    签名成功,需要返回下面几个参数给前端作验签使用。

    参数名 类型 说明
    timestamp int 生成签名的时间戳
    noncestr string 生成签名的随机串
    signature string 签名

    废话不多说,直接上后端签名代码:

    1. 1 static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();//日志
    2. 2
    3. 3 public WXShare GetWxShareInfo(string url)
    4. 4 {
    5. 5 DateTime now = DateTime.Now;
    6. 6 var timestamp = DateTimeHelper.GetTimeStamp(now);//取十位时间戳
    7. 7 var guid = Guid.NewGuid().ToString("N");//随机串
    8. 8 var ticket = "";//签名密钥
    9. 9 try {
    10. 10 WXShare s= new WXShare();
    11. 11 //取缓存中的Ticket,没有则重新生成Ticket值(也可以将Ticket值保存到文件中,此时从文件中读取Ticket)
    12. 12 WxShareCache Cache = new WxShareCache(Key).GetCache<WxShareCache>();
    13. 13 if (Cache == null || string.IsNullOrWhiteSpace(Cache.Ticket)) {
    14. 14 Cache = new WxShareCache(Key);
    15. 15 Cache.Ticket = GetTicket();//获取Ticket值
    16. 16 Cache.SetCache(Cache);//添加缓存
    17. 17 ticket = Cache.Ticket;
    18. 18 } else {
    19. 19 ticket = Cache.Ticket;
    20. 20 }
    21. 21 url = HttpUtility.UrlDecode(url);//url解码
    22. 22 string sign = GetSignature(ticket,guid,timestamp,url);
    23. 23 s.noncestr = guid;
    24. 24 s.timestamp = timestamp;
    25. 25 s.sign = sign;
    26. 26 logger.Warn($"url:{url},时间戳:{timestamp},随机数:{guid},ticket:{ticket},sign值:{sign}");//记录日志
    27. 27 return s;
    28. 28 } catch (Exception ex) {
    29. 29 logger.Warn(ex);
    30. 30 throw ex;
    31. 31 }
    32. 32 }
    复制代码

    返回给前端的对象

    1. /// <summary>
    2. /// 返回实体
    3. /// </summary>
    4. public class WXShare
    5. {
    6. /// <summary>
    7. /// 随机码
    8. /// </summary>
    9. public string noncestr { get; set; }
    10. /// <summary>
    11. /// 时间戳
    12. /// </summary>
    13. public int timestamp { get; set; }
    14. /// <summary>
    15. /// 签名值
    16. /// </summary>
    17. public string signature { get; set; }
    18. }
    复制代码

    时间戳

    1. /// <summary>
    2. /// 十位时间戳
    3. /// </summary>
    4. /// <param name="dt"></param>
    5. /// <returns></returns>
    6. public static int GetTimeStamp(DateTime dt)
    7. {
    8. DateTime dateStart = new DateTime(1970, 1, 1, 8, 0, 0);
    9. int timeStamp = Convert.ToInt32((dt - dateStart).TotalSeconds);
    10. return timeStamp;
    11. }
    复制代码

    请求方法

    1. //请求基类
    2. private static HttpClient _client = null;
    3. public static HttpClient Client {
    4. get {
    5. if (_client == null) {
    6. var handler = new HttpClientHandler() {
    7. AutomaticDecompression = DecompressionMethods.GZip,
    8. AllowAutoRedirect = false,
    9. UseCookies = false,
    10. };
    11. _client = new HttpClient(handler);
    12. _client.Timeout = TimeSpan.FromSeconds(5);
    13. _client.DefaultRequestHeaders.Add("Accept","application/json");
    14. }
    15. return _client;
    16. }
    17. }
    复制代码

    签名密钥

    1. /// <summary>
    2. /// GetTicket
    3. /// </summary>
    4. /// <returns></returns>
    5. public static string GetTicket()
    6. {
    7. string token = GetAccessToken();//获取AccessToken
    8. IDictionary<string,string> dic = new Dictionary<string,string>();
    9. dic["access_token"] = token;
    10. dic["type"] = "jsapi";
    11. FormUrlEncodedContent content = new FormUrlEncodedContent(dic);
    12. var response = Client.PostAsync("https://api.weixin.qq.com/cgi-bin/ticket/getticket",content).Result;
    13. if (response.StatusCode != HttpStatusCode.OK)
    14. return "";
    15. var result = response.Content.ReadAsStringAsync().Result;
    16. JObject obj = JObject.Parse(result);
    17. string ticket = obj["ticket"]?.ToString()??"";
    18. return ticket;
    19. }
    复制代码

    AccessToken

    1. /// <summary>
    2. /// GetAccessToken
    3. /// </summary>
    4. /// <returns></returns>
    5. public static string GetAccessToken()
    6. {
    7. IDictionary<string,string> dic = new Dictionary<string,string>();
    8. dic["grant_type"] = "client_credential";
    9. dic["appid"] = "";//自己的appid
    10. dic["secret"] = "";//自己的appsecret
    11. FormUrlEncodedContent content = new FormUrlEncodedContent(dic);
    12. var response = Client.PostAsync("https://api.weixin.qq.com/cgi-bin/token",content).Result;
    13. if (response.StatusCode != HttpStatusCode.OK)
    14. return "";
    15. var result = response.Content.ReadAsStringAsync().Result;
    16. JObject obj = JObject.Parse(result);
    17. string token = obj["access_token"]?.ToString()??"";
    18. return token;
    19. }
    复制代码

    签名算法

    1. /// <summary>
    2. /// 签名算法
    3. /// </summary>
    4. /// <param name="ticket">ticket</param>
    5. /// <param name="noncestr">随机字符串</param>
    6. /// <param name="timestamp">时间戳</param>
    7. /// <param name="url"></param>
    8. /// <returns></returns>
    9. public static string GetSignature(string ticket,string noncestr,long timestamp,string url)
    10. {
    11. var string1Builder = new StringBuilder();
    12. //拼接字符串
    13. string1Builder.Append("jsapi_ticket=").Append(ticket).Append("&")
    14. .Append("noncestr=").Append(noncestr).Append("&")
    15. .Append("timestamp=").Append(timestamp).Append("&")
    16. .Append("url=").Append(url.IndexOf("#") >= 0 ? url.Substring(0,url.IndexOf("#")) : url);
    17. string str = string1Builder.ToString();
    18. return SHA1(str);//加密
    19. }
    复制代码

    SHA1加密

    1. public static string SHA1(string content)
    2. {
    3. return SHA1(content,Encoding.UTF8);
    4. }
    5. /// <summary>
    6. /// SHA1 加密
    7. /// </summary>
    8. /// <param name="content">需要加密字符串</param>
    9. /// <param name="encode">指定加密编码</param>
    10. /// <returns>返回40位小写字符串</returns>
    11. public static string SHA1(string content,Encoding encode)
    12. {
    13. try {
    14. SHA1 sha1 = new SHA1CryptoServiceProvider();
    15. byte[] bytes_in = encode.GetBytes(content);
    16. byte[] bytes_out = sha1.ComputeHash(bytes_in);
    17. sha1.Dispose();
    18. string result = BitConverter.ToString(bytes_out);
    19. result = result.Replace("-","").ToLower();//转小写
    20. return result;
    21. } catch (Exception ex) {
    22. throw new Exception("SHA1加密出错:" + ex.Message);
    23. }
    24. }
    复制代码

    前端验签

    1.引入JS文件

    在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.4.0.js

    2.调用后端接口并注入权限验证

    1. $(function(){
    2. var url=encodeURIComponent(location.href.split('#')[0]); //对当前url编码
    3. //ajax注入权限验证
    4. $.ajax({
    5. url:"ajax",
    6. type:'GET',
    7. data: {url:url},
    8. error: function(XMLHttpRequest, textStatus, errorThrown){
    9. alert("发生错误:"+errorThrown);
    10. },
    11. success: function(res){
    12. var appId = "";//与后端的appid相同
    13. var noncestr = res.noncestr;
    14. var timestamp = res.timestamp;
    15. var signature = res.signature;
    16. wx.config({
    17. debug: true, //开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    18. appId: appId, //必填,公众号的唯一标识
    19. timestamp: timestamp, // 必填,生成签名的时间戳
    20. nonceStr: noncestr, //必填,生成签名的随机串
    21. signature: signature,// 必填,签名
    22. jsApiList: ['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ',
    23. 'onMenuShareWeibo','onMenuShareQZone','chooseImage',
    24. 'uploadImage','downloadImage','startRecord','stopRecord',
    25. 'onVoiceRecordEnd','playVoice','pauseVoice','stopVoice',
    26. 'translateVoice','openLocation','getLocation','hideOptionMenu',
    27. 'showOptionMenu','closeWindow','hideMenuItems','showMenuItems',
    28. 'showAllNonBaseMenuItem','hideAllNonBaseMenuItem','scanQRCode'] //必填,需要使用的JS接口列表,所有JS接口列表
    29. });
    30. }
    31. });
    32. });
    复制代码

    至此,后端签名,前端验签过程结束。

    在这过程中,掉过几次坑。最让我印象深刻的是测试的时候怎么样都是签名错误(返回invalid signature)。考虑到可能是url的问题,所以在前端做了编码,后端做了解码,然后验签成功。

    测试签名正确与否,微信有个校验工具,如下:

    https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign

    将签名的四个参数输入,生成的签名与后端生成的签名作对比,sign值一致说明签名是正确的,不一致就需要检查程序逻辑问题了。


    来源:https://www.cnblogs.com/p1024q/p/11323631.html
    免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则