微信JSSDK簽名

来源:https://www.cnblogs.com/p1024q/archive/2019/08/08/11323631.html
-Advertisement-
Play Games

微信JS-SDK說明文檔 https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115 生成簽名 1.簽名規則 參與簽名的欄位包括noncestr(隨機字元串), 有效的jsapi_ticket, timestamp(時間戳), ...


微信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:隨機字元串,可以直接生成。

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

https請求方式: GET
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數據

{"access_token":"ACCESS_TOKEN","expires_in":7200}

    其中, access_token的值就是我們要用的值,expires_in 是憑證有效時間(7200秒)

    取到access_token後,要將值存到緩存不大於7200秒,再調用下麵的介面

http請求方式: GET
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi

   成功則返回如下JSON

{
"errcode":0,
"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
"expires_in":7200
}

   ticket值就是要用的jsapi_ticket。

    timestamp:時間戳.。

    url:當前網頁的URL,不包含#及其後面部分。作為介面請求參數通過前端傳過來。

   有了這四個值,就能根據簽名規則進行簽名,得到簽名值signature。

   簽名成功,需要返回下麵幾個參數給前端作驗簽使用。

參數名 類型 說明
timestamp int 生成簽名的時間戳
noncestr string 生成簽名的隨機串
signature string 簽名

 

 

 

 

   廢話不多說,直接上後端簽名代碼:

 1 static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();//日誌
 2 
 3 public WXShare GetWxShareInfo(string url)
 4         {
 5             DateTime now = DateTime.Now;
 6             var timestamp = DateTimeHelper.GetTimeStamp(now);//取十位時間戳
 7             var guid = Guid.NewGuid().ToString("N");//隨機串
 8             var ticket = "";//簽名密鑰
 9             try {
10                 WXShare s= new WXShare();
11 //取緩存中的Ticket,沒有則重新生成Ticket值(也可以將Ticket值保存到文件中,此時從文件中讀取Ticket)
12                 WxShareCache Cache = new WxShareCache(Key).GetCache<WxShareCache>();
13                 if (Cache == null || string.IsNullOrWhiteSpace(Cache.Ticket)) {
14                     Cache = new WxShareCache(Key);
15                     Cache.Ticket = GetTicket();//獲取Ticket值
16                     Cache.SetCache(Cache);//添加緩存
17                     ticket = Cache.Ticket;
18                 } else {
19                     ticket = Cache.Ticket;
20                 }
21                 url = HttpUtility.UrlDecode(url);//url解碼
22                 string sign = GetSignature(ticket,guid,timestamp,url);
23                 s.noncestr = guid;
24                 s.timestamp = timestamp;
25                 s.sign = sign;
26                 logger.Warn($"url:{url},時間戳:{timestamp},隨機數:{guid},ticket:{ticket},sign值:{sign}");//記錄日誌
27                 return s;
28             } catch (Exception ex) {
29                 logger.Warn(ex);
30                 throw ex;
31             }
32         }

 

   返回給前端的對象

        /// <summary>
        /// 返回實體
        /// </summary>
        public class WXShare
        {
            /// <summary>
            /// 隨機碼
            /// </summary>
            public string noncestr { get; set; }
            /// <summary>
            /// 時間戳
            /// </summary>
            public int timestamp { get; set; }
           /// <summary>
           /// 簽名值
           /// </summary>
            public string signature { get; set; }
        }

 

時間戳

        /// <summary>
        /// 十位時間戳
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static int GetTimeStamp(DateTime dt)
        {
            DateTime dateStart = new DateTime(1970, 1, 1, 8, 0, 0);
            int timeStamp = Convert.ToInt32((dt - dateStart).TotalSeconds);
            return timeStamp;
        }

 

請求方法

        //請求基類
        private static HttpClient _client = null;
        public static HttpClient Client {
            get {
                if (_client == null) {
                    var handler = new HttpClientHandler() {
                        AutomaticDecompression = DecompressionMethods.GZip,
                        AllowAutoRedirect = false,
                        UseCookies = false,
                    };
                    _client = new HttpClient(handler);
                    _client.Timeout = TimeSpan.FromSeconds(5);
                    _client.DefaultRequestHeaders.Add("Accept","application/json");

                }
                return _client;
            }
        }

 

簽名密鑰

        /// <summary>
        /// GetTicket
        /// </summary>
        /// <returns></returns>
        public static string GetTicket()
        {
            string token = GetAccessToken();//獲取AccessToken
            IDictionary<string,string> dic = new Dictionary<string,string>();
            dic["access_token"] = token;
            dic["type"] = "jsapi";
            FormUrlEncodedContent content = new FormUrlEncodedContent(dic);
            var response = Client.PostAsync("https://api.weixin.qq.com/cgi-bin/ticket/getticket",content).Result;
            if (response.StatusCode != HttpStatusCode.OK)
                return "";
            var result = response.Content.ReadAsStringAsync().Result;
            JObject obj = JObject.Parse(result);
            string ticket = obj["ticket"]?.ToString()??"";
            return ticket;
        }

 

AccessToken

        /// <summary>
        /// GetAccessToken
        /// </summary>
        /// <returns></returns>
        public static string GetAccessToken()
        {
            IDictionary<string,string> dic = new Dictionary<string,string>();
            dic["grant_type"] = "client_credential";
            dic["appid"] = "";//自己的appid
            dic["secret"] = "";//自己的appsecret

            FormUrlEncodedContent content = new FormUrlEncodedContent(dic);
            var response = Client.PostAsync("https://api.weixin.qq.com/cgi-bin/token",content).Result;
            if (response.StatusCode != HttpStatusCode.OK)
                return "";
            var result = response.Content.ReadAsStringAsync().Result;
            JObject obj = JObject.Parse(result);
            string token = obj["access_token"]?.ToString()??"";
            return token;
        }

 

簽名演算法

        /// <summary>
        /// 簽名演算法
        /// </summary>
        /// <param name="ticket">ticket</param>
        /// <param name="noncestr">隨機字元串</param>
        /// <param name="timestamp">時間戳</param>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string GetSignature(string ticket,string noncestr,long timestamp,string url)
        {
            var string1Builder = new StringBuilder();
            //拼接字元串
            string1Builder.Append("jsapi_ticket=").Append(ticket).Append("&")
                          .Append("noncestr=").Append(noncestr).Append("&")
                          .Append("timestamp=").Append(timestamp).Append("&")
                          .Append("url=").Append(url.IndexOf("#") >= 0 ? url.Substring(0,url.IndexOf("#")) : url);
            string str = string1Builder.ToString();
            return SHA1(str);//加密
        }

 

SHA1加密

        
        public static string SHA1(string content)
        {
            return SHA1(content,Encoding.UTF8);
        }

        /// <summary>
        /// SHA1 加密
        /// </summary>
        /// <param name="content">需要加密字元串</param>
        /// <param name="encode">指定加密編碼</param>
        /// <returns>返回40位小寫字元串</returns>
        public static string SHA1(string content,Encoding encode)
        {
            try {
                SHA1 sha1 = new SHA1CryptoServiceProvider();
                byte[] bytes_in = encode.GetBytes(content);
                byte[] bytes_out = sha1.ComputeHash(bytes_in);
                sha1.Dispose();
                string result = BitConverter.ToString(bytes_out);
                result = result.Replace("-","").ToLower();//轉小寫
                return result;
            } catch (Exception ex) {
                throw new Exception("SHA1加密出錯:" + ex.Message);
            }
        }

 

 

前端驗簽

   1.引入JS文件

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

   2.調用後端介面並註入許可權驗證

$(function(){  
    var url=encodeURIComponent(location.href.split('#')[0]); //對當前url編碼
    //ajax註入許可權驗證  
    $.ajax({  
        url:"ajax",  
        type:'GET',
        data: {url:url},   
        error: function(XMLHttpRequest, textStatus, errorThrown){  
            alert("發生錯誤:"+errorThrown);  
        },  
        success: function(res){
            var appId = "";//與後端的appid相同  
            var noncestr = res.noncestr;  
            var timestamp = res.timestamp;  
            var signature = res.signature;  
            wx.config({  
                debug: true, //開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會列印。  
                appId: appId, //必填,公眾號的唯一標識  
                timestamp: timestamp, // 必填,生成簽名的時間戳  
                nonceStr: noncestr, //必填,生成簽名的隨機串  
                signature: signature,// 必填,簽名  
                jsApiList: ['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ',  
                            'onMenuShareWeibo','onMenuShareQZone','chooseImage',  
                            'uploadImage','downloadImage','startRecord','stopRecord',  
                            'onVoiceRecordEnd','playVoice','pauseVoice','stopVoice',  
                            'translateVoice','openLocation','getLocation','hideOptionMenu',  
                            'showOptionMenu','closeWindow','hideMenuItems','showMenuItems',  
                            'showAllNonBaseMenuItem','hideAllNonBaseMenuItem','scanQRCode'] //必填,需要使用的JS介面列表,所有JS介面列表   
            });  
        }  
    });  
  
}); 

 

 

    至此,後端簽名,前端驗簽過程結束。

    在這過程中,掉過幾次坑。最讓我印象深刻的是測試的時候怎麼樣都是簽名錯誤(返回invalid signature)。考慮到可能是url的問題,所以在前端做了編碼,後端做瞭解碼,然後驗簽成功。

    測試簽名正確與否,微信有個校驗工具,如下:

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

    將簽名的四個參數輸入,生成的簽名與後端生成的簽名作對比,sign值一致說明簽名是正確的,不一致就需要檢查程式邏輯問題了。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 出自公眾號:大飛碼字 “怎樣才能進大廠?” “我是一個二本電腦專業的學生,怎樣才能進大廠?” “我是一個非電腦專業的學生,有機會可以進大廠嗎?” 我收到不少咨詢如何進大廠的問題,以上是一些比較典型的提問。 “怎麼可以進大廠” 這個問題,跟“怎麼可以在30歲前賺到1000萬” 和 “怎麼可以在40 ...
  • 問題描述 每一本正式出版的圖書都有一個ISBN號碼與之對應,ISBN碼包括9位數字、1位識別碼和3位分隔符,其規定格式如“x-xxx-xxxxx-x”,其中符號“-”是分隔符(鍵盤上的減號),最後一位是識別碼,例如0-670-82162-4就是一個標準的ISBN碼。ISBN碼的首位數字表示書籍的出版 ...
  • 10.17 內置函數 強調:mysql內置的函數只能在sql語句中使用 date_format: 10.171 自定義函數 函數中不要寫sql語句(否則會報錯),函數僅僅只是一個功能,是一個在sql中被應用的功能,若要想在begin...end...中寫sql,請用存儲過程. 刪除函數: 執行函數: ...
  • 4.7 推導式 列表推導式 基本格式 v1 = [i for i in 可迭代對象 ]v2 = [i for i in 可迭代對象 if 條件 ] # 條件為true才進行append 1 # 示例一 2 v1 = [ i for i in 'alex' ] # ['a','l','e','x'] ...
  • 最近趕項目忽然想到一個問題,那就是在 .Net平臺下的C#代碼是怎麼從源代碼到機器可以識別的電腦的(只怪自己上學不好好讀書,現在又要重補一遍了!!!) 話不多說直接上調研結果: 預習知識: 1: IL 是微軟.NET平臺上衍生出的一門中間語言,.NET平臺上的各種高級語言(如C#,VB,F#)的編譯 ...
  • 圖片中物體(人物,動物或其它特定物品)的精確識別與提取是人工智慧領域重要的一個方面,通過機器學習,最終能達到不需要人工干預準確的進行識別。 以雲服務的方式提供 由於這些演算法依賴於大量的訓練或基礎數據,所以,對於一些成果,以靜態的演算法,每個應用獨立去完成漫漫的訓練不是個好辦法。因此,很多類似的成果會以 ...
  • 前面已經介紹過了 ".Net Core 程式發佈到 Docker 容器" 的內容。但是每次通過 SSH 鏈接到伺服器敲命令,運行腳本也是挺麻煩的一件事。程式員是最懶的,能讓電腦解決的問題絕不手動解決,如果當我們push一次代碼後自動build代碼,自動跑單元測試,如果測試通過,自動發佈程式,如果失敗 ...
  • SOA體系架構 微服務架構 微服務特點 微服務資料庫設計 傳統單一的中心化資料庫和微服務一個服務一個資料庫 微服務和限界上下文模式的關係 微服務的邏輯架構和物理架構 分散式數據管理的挑戰和解決方案 挑戰 1:如何定義微服務邊界 首先,需要關註應用的邏輯領域模型和相關數據。必須嘗試識別同一個應用中解耦 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...