最近項目移動端需要實現微信自定義分享功能,包含分享自定義標題、描述等。 首先到公眾號的後臺,功能設置裡面,添加將要被分享的功能變數名稱,如圖 後端簽名演算法實現 ,參考騰訊開發者文檔https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp142114111 ...
最近項目移動端需要實現微信自定義分享功能,包含分享自定義標題、描述等。
- 首先到公眾號的後臺,功能設置裡面,添加將要被分享的功能變數名稱,如圖
- 後端簽名演算法實現 ,參考騰訊開發者文檔https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
jsapi_ticket
生成簽名之前必須先瞭解一下jsapi_ticket,jsapi_ticket是公眾號用於調用微信JS介面的臨時票據。正常情況下,jsapi_ticket的有效期為7200秒,通過access_token來獲取。由於獲取jsapi_ticket的api調用次數非常有限,頻繁刷新jsapi_ticket會導致api調用受限,影響自身業務,開發者必須在自己的服務全局緩存jsapi_ticket 。
生成簽名步驟,獲取AccessToken-》獲取JsApiTicket-》生成簽名
appsettings配置文件添加配置
"Api": {
"JSJDKBaseApi": "https://api.weixin.qq.com/cgi-bin"
},
"JSJDK": {
"AppId": "***",
"Secret": "****"
}
- 創建簽名公用類
public class JSSDKSignHelper { private static AccessTokenResponse singleAccessToken; private string _tencentApi { get; set; } private string _appid { get; set; } private string _appSecret { get; set; } public JSSDKSignHelper(IConfiguration config) { _tencentApi = config["Api:JSJDKBaseApi"]; _appid = config["JSJDK:AppId"]; _appSecret = config["JSJDK:Secret"]; } public string GetAccessTokenSingle(out bool isNewObj) { isNewObj = false; if (singleAccessToken != null && (singleAccessToken.expire_out > DateTime.Now.AddHours(2))) { return singleAccessToken.access_token; } else { var response = GetAccessToken(); if (response.errcode == 0) { response.expire_out = DateTime.Now.AddHours(expireHour); singleAccessToken = response; isNewObj = true; return response?.access_token; } } return null; } /// <summary> /// 獲取access_token /// </summary> /// <returns></returns> private AccessTokenResponse GetAccessToken() { string url = _tencentApi + "/token?grant_type=client_credential&appid=" + _appid + "&secret=" + _appSecret; var response = ApiClient.GetJson<AccessTokenResponse>(url); return response; } /// <summary> /// 獲取JsApiTicket /// </summary> /// <param name="accessToken"></param> /// <returns></returns> public string GetJsApiTicket(string accessToken) { if (string.IsNullOrWhiteSpace(accessToken)) { return null; } string url = _tencentApi + $"/ticket/getticket?type=jsapi&access_token={accessToken}"; var response = ApiClient.GetJson<AccessTicketResponse>(url); return response?.ticket; } /// <summary> /// 獲取簽名 /// </summary> /// <param name="jsapi_ticket"></param> /// <param name="noncestr">隨機字元串(必須與wx.config中的nonceStr相同)</param> /// <param name="timestamp">時間戳(必須與wx.config中的timestamp相同)</param> /// <param name="url">當前網頁的URL,不包含#及其後面部分(必須是調用JS介面頁面的完整URL)</param> /// <returns></returns> public string GetSignature(string jsapi_ticket, string noncestr, string timestamp, string url) { if (string.IsNullOrEmpty(jsapi_ticket) || string.IsNullOrEmpty(noncestr) || string.IsNullOrEmpty(timestamp) || string.IsNullOrEmpty(url)) return null; var string1Builder = new StringBuilder(); string1Builder.Append("jsapi_ticket=").Append(jsapi_ticket).Append("&") .Append("noncestr=").Append(noncestr).Append("&") .Append("timestamp=").Append(timestamp).Append("&") .Append("url=").Append(url.IndexOf("#") >= 0 ? url.Substring(0, url.IndexOf("#")) : url); return Sha1Sign(string1Builder.ToString()); } /// <summary> /// Sha1加密簽名 /// </summary> /// <param name="str"></param> /// <returns></returns> public string Sha1Sign(string str) { SHA1 sha1 = new SHA1CryptoServiceProvider(); byte[] bytes_sha1_in = System.Text.UTF8Encoding.Default.GetBytes(str); byte[] bytes_sha1_out = sha1.ComputeHash(bytes_sha1_in); string signature = BitConverter.ToString(bytes_sha1_out); signature = signature.Replace("-", "").ToLower(); return signature; } }
- 簽名定義的Model
public class AccessTokenResponse: BaseResponse { /// <summary> /// 返回access_token值,有效期7200秒 /// </summary> public string access_token { get; set; } /// <summary> /// 過期時間,單位小時 /// </summary> public DateTime expire_out { get; set; } } public class AccessTicketResponse : BaseResponse { /// <summary> /// 返回Ticket票據,有效期7200秒 /// </summary> public string ticket { get; set; } } public class BaseResponse { /// <summary> /// 過期時間,單位秒 /// </summary> public string expire_in { get; set; } /// <summary> /// 錯誤代碼 /// </summary> public Int32 errcode { get; set; } /// <summary> /// 錯誤信息描述 /// </summary> public string errmsg { get; set; } }
- 創建生成簽名的時間戳和隨機串
/// <summary> /// 獲取微信JS-JDK時間戳 /// </summary> /// <returns></returns> public static string GetTimeStamp() { TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0); return Convert.ToInt64(ts.TotalSeconds).ToString(); } /// <summary> /// JS-JDK 創建隨機字元串 /// </summary> /// <returns></returns> public static string CreatenNonce_str() { Random r = new Random(); var sb = new StringBuilder(); var length = strs.Length; for (int i = 0; i < 15; i++) { sb.Append(strs[r.Next(length - 1)]); } return sb.ToString(); }
- Action中實際調用
[HttpPost] [Route("GetSignature")] public string GetSignature(string url= "http://www.sina.cn/") { try { if (string.IsNullOrEmpty(url)) return "url不能為空"; string _jsTicket = null, _signature = null; string _accessToken = _jsSignHelper.GetAccessTokenSingle(out bool isNewObj); WxConfigModel model = new WxConfigModel(); model.AppId = appId; model.TimeStamp = MyUtil.GetTimeStamp(); model.NonceStr = MyUtil.CreatenNonce_str(); if (isNewObj|| _wxConfigModel.Signature==null) { _jsTicket = _jsSignHelper.GetJsApiTicket(_accessToken); _signature = _jsSignHelper.GetSignature(_jsTicket, model.NonceStr, model.TimeStamp, url); model.jsTicket = _jsTicket; model.Signature = _signature; _wxConfigModel = model; } return JsonConvert.SerializeObject(model); } catch (Exception ex) { throw ex; } }
註意:
- 簽名用的noncestr和timestamp必須與wx.config中的nonceStr和timestamp相同。
- 移動端分享時,不能本地測試,需要發佈到外網測試,否則報簽名錯誤
- 生成的簽名,可以實用校驗工具校驗是否正確。https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign