由于 官方文档示例代码 里面没有包含C#版本的代码,本文主要分享C#版本的微信JS-SDK签名处理,所以这里进行简单分享。点击查看 源码地址 。
微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包。
通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验。
首先,可以点击查看 获取token 的官方描述。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。由于 接口频率限制 ,开发者必须在自己的服务全局缓存jsapi_ticket。
/// <summary> /// 获取AccessToken_token /// </summary> /// <returns></returns> public static AccessToken Getaccess() { string Str = GetJson(string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", wxAppId, wxAppSecret)); AccessToken m = JsonHelper.ParseFromJson<AccessToken>(Str); return m; } /// 获取token,如果存在且没过期,则直接取token /// <summary> /// 获取token,如果存在且没过期,则直接取token /// </summary> /// <returns></returns> public static string GetExistAccessToken() { // 读取XML文件中的数据 string filepath = System.Web.HttpContext.Current.Server.MapPath("/XMLToken.xml"); FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); StreamReader str = new StreamReader(fs, System.Text.Encoding.UTF8); XmlDocument xml = new XmlDocument(); xml.Load(str); str.Close(); str.Dispose(); fs.Close(); fs.Dispose(); string Token = xml.SelectSingleNode("xml").SelectSingleNode("AccessToken").InnerText; DateTime AccessTokenExpires = Convert.ToDateTime(xml.SelectSingleNode("xml").SelectSingleNode("AccessExpires").InnerText); //如果token过期,则重新获取token if (DateTime.Now >= AccessTokenExpires) { AccessToken mode = Getaccess(); //将token存到xml文件中,全局缓存 xml.SelectSingleNode("xml").SelectSingleNode("AccessToken").InnerText = mode.access_token; DateTime _AccessTokenExpires = DateTime.Now.AddSeconds(mode.expires_in); xml.SelectSingleNode("xml").SelectSingleNode("AccessExpires").InnerText = _AccessTokenExpires.ToString(); xml.Save(filepath); Token = mode.access_token; } return Token; }
生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。
/// 获取jsapi_ticket /// <summary> /// 获取jsapi_ticket /// </summary> /// <param name="access"></param> /// <returns></returns> private static JSTicket getJSTicketTicket(string access) { string Str = GetJson("https://api.weixin.qq.com/cgi-bin/ticket/getticket?AccessToken_token=" + access + "&type=JSTicket"); JSTicket jt = JsonConvert.DeserializeObject<JSTicket>(Str); return jt; } /// <summary> /// 获取jsapi_ticket,如果存在且没过期,则直接取jsapi_ticket /// </summary> /// <returns></returns> public static string GetExistJSTicket() { // 读取XML文件中的数据 string filepath = System.Web.HttpContext.Current.Server.MapPath("/XMLToken.xml"); FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); StreamReader str = new StreamReader(fs, System.Text.Encoding.UTF8); XmlDocument xml = new XmlDocument(); xml.Load(str); str.Close(); str.Dispose(); fs.Close(); fs.Dispose(); string ticket = xml.SelectSingleNode("xml").SelectSingleNode("JSTicket").InnerText; DateTime AccessTokenExpires = Convert.ToDateTime(xml.SelectSingleNode("xml").SelectSingleNode("JSAccessExpires").InnerText); //如果jsapi_ticket过期,则重新获取token if (DateTime.Now >= AccessTokenExpires) { string Token = xml.SelectSingleNode("xml").SelectSingleNode("AccessToken").InnerText; JSTicket mode = getJSTicketTicket(Token); if (mode.ticket != null && mode.expires_in != null) { //将jsapi_ticket存到xml文件中,全局缓存 xml.SelectSingleNode("xml").SelectSingleNode("JSTicket").InnerText = mode.ticket; DateTime _AccessTokenExpires = DateTime.Now.AddSeconds(int.Parse(mode.expires_in)); xml.SelectSingleNode("xml").SelectSingleNode("JSAccessExpires").InnerText = _AccessTokenExpires.ToString(); xml.Save(filepath); ticket = mode.ticket; } else { ticket = ""; } } return ticket; }
/// <summary> /// 创建随机字符串 /// </summary> /// <returns></returns> private static string createNonceStr() { int length = 16; string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; string str = ""; Random rad = new Random(); for (int i = 0; i < length; i++) { str += chars.Substring(rad.Next(0, chars.Length - 1), 1); } return str; }
/// 将c# DateTime时间格式转换为Unix时间戳格式 /// <summary> /// 将c# DateTime时间格式转换为Unix时间戳格式 /// </summary> /// <param name="time">时间</param> /// <returns>double</returns> public static int ConvertDateTimeInt(System.DateTime time) { int intResult = 0; System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1)); intResult = Convert.ToInt32((time - startTime).TotalSeconds); return intResult; }
签名生成规则 如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。
即 signature=sha1(string1)
。 示例:
noncestr=Wm3WZYTPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
timestamp=1414587457
url= http://mp.weixin.qq.com?params=value
步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com?params=value
步骤2. 对string1进行sha1签名,得到signature:
0f9de62fce790f9a083d5c99e95740ceb90c27ed
注意:生成签名的url参数不能包含中文字符,中文字符需要单独转义。不过源码中已经用HttpContext.Current.Request.Url.AbsoluteUri进行处理。
下面是生成签名的代码:
// 这里参数的顺序要按照 key 值 ASCII 码升序排序 string rawstring = "JSTicket_ticket=" + JSTicketTicket + "&noncestr=" + nonceStr + "×tamp=" + timestamp + "&url=" + url + ""; string signature = FormsAuthentication.HashPasswordForStoringInConfigFile(rawstring, "SHA1").ToLower();
/// 得到数据包,返回使用页面 /// <summary> /// 得到数据包,返回使用页面 /// </summary> /// <returns></returns> public static Hashtable getSignPackage() { //AccessToken ace = Getaccess(); string token = GetExistAccessToken(); string JSTicketTicket = GetExistJSTicket(); string url = HttpContext.Current.Request.Url.AbsoluteUri; string timestamp = Convert.ToString(ConvertDateTimeInt(DateTime.Now)); string nonceStr = createNonceStr(); // 这里参数的顺序要按照 key 值 ASCII 码升序排序 string rawstring = "JSTicket_ticket=" + JSTicketTicket + "&noncestr=" + nonceStr + "×tamp=" + timestamp + "&url=" + url + ""; string signature = FormsAuthentication.HashPasswordForStoringInConfigFile(rawstring, "SHA1").ToLower(); //string signature = SHA1_Hash(rawstring); Hashtable signPackage = new Hashtable(); signPackage.Add("appId", wxAppId); signPackage.Add("nonceStr", nonceStr); signPackage.Add("timestamp", timestamp); signPackage.Add("url", url); signPackage.Add("signature", signature); signPackage.Add("rawString", rawstring); signPackage.Add("JSTicketTicket", JSTicketTicket); return signPackage; }
有问题认真查看文档或者源码里面提Issues。
============================================