微信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":}

其中, 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":,
"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
"expires_in":
}

ticket值就是要用的jsapi_ticket。

timestamp:时间戳.。

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

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

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

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

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

 static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();//日志

 public WXShare GetWxShareInfo(string url)
{
DateTime now = DateTime.Now;
var timestamp = DateTimeHelper.GetTimeStamp(now);//取十位时间戳
var guid = Guid.NewGuid().ToString("N");//随机串
var ticket = "";//签名密钥
try {
WXShare s= new WXShare();
//取缓存中的Ticket,没有则重新生成Ticket值(也可以将Ticket值保存到文件中,此时从文件中读取Ticket)
WxShareCache Cache = new WxShareCache(Key).GetCache<WxShareCache>();
if (Cache == null || string.IsNullOrWhiteSpace(Cache.Ticket)) {
Cache = new WxShareCache(Key);
Cache.Ticket = GetTicket();//获取Ticket值
Cache.SetCache(Cache);//添加缓存
ticket = Cache.Ticket;
} else {
ticket = Cache.Ticket;
}
url = HttpUtility.UrlDecode(url);//url解码
string sign = GetSignature(ticket,guid,timestamp,url);
s.noncestr = guid;
s.timestamp = timestamp;
s.sign = sign;
logger.Warn($"url:{url},时间戳:{timestamp},随机数:{guid},ticket:{ticket},sign值:{sign}");//记录日志
return s;
} catch (Exception ex) {
logger.Warn(ex);
throw ex;
}
}

返回给前端的对象

        /// <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(, , , , , );
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();
_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("#") >= ? url.Substring(,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值一致说明签名是正确的,不一致就需要检查程序逻辑问题了。

微信JSSDK签名的更多相关文章

  1. 微信JS-SDK签名signature错误代码4029

    一般是url的错,url需要动态获取 c#的写法: //Request.Url.ToString()这个获取的是没有端口号的有些网站有端口号 string url = Request.Url.Orig ...

  2. vue项目中微信jssdk在ios签名失败

    一.问题描述 1. vue项目中微信jssdk签名时,在安卓和ios是有差异的,签名时使用的url=window.location.href.split('#')[0],此时在安卓没问题,在ios会导 ...

  3. 微信js-sdk开发获取签名和获取地理位置接口示例

    ###微信js-sdk开发获取签名和获取地理位置接口示例 前言:在做微信公众号开发时需要获取用户的地理位置信息,之前通过高德或者百度.腾讯等地图的api时发现经常获取不到,毕竟第三方的东西,后来改为采 ...

  4. 调用微信JS-SDK配置签名

    前后端进行分开开发: 1:后端实现获取 +++接口凭证:access_token (公众号的全局唯一接口调用凭据) ** GET 获取:https://api.weixin.qq.com/cgi-bi ...

  5. 微信JSSDK权限签名申请

    前提: 1.绑定域名 先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”. 里边有说明(这里提示一点:需要把当前公众号的验证文件放到指定目录下) 2.需要参数: APPID. ...

  6. php 微信jssdk 微信分享一直报config:fail,Error: invalid signature(签名生成是一致的)

    php 微信jssdk 微信分享一直报config:fail,Error: invalid signature(签名生成是一致的) 里面url必须是当前的url比方说在A地址 请求获取jssdk参数 ...

  7. 微信开发之c#下jssdk签名生成

    参考文章 :微信JS-SDK 权限签名算法 C#版 这篇文章讲解的的比较详细,而且算法准确,但是这篇文章有几个错误的地方需要注意; url必须动态生成 url不能写死,否则就算结果和官方检测的一致,也 ...

  8. C#开发微信门户及应用(39)--使用微信JSSDK实现签到的功能

    随着微信开逐步开放更多JSSDK的接口,我们可以利用自定义网页的方式来调用更多微信的接口,实现我们更加丰富的界面功能和效果,例如我们可以在页面中调用各种手机的硬件来获取信息,如摄像头拍照,GPS信息. ...

  9. 微信JS-SDK坐标位置转换为百度地图坐标

    微信JS-SDK开发过程中,使用getLocation获取坐标位置,如何将微信获取的坐标直接应用到百度地图中,显示以下效果: 说明:红色图标是从微信转换过来的位置,蓝色图标是周边位置.首先从微信开发流 ...

随机推荐

  1. Spring中的配置文件文件位置

    在Java开发中,一般在Spring框架中,classpath的位置是指src下,在IDEA中一般是指resource中 配置文件 位置:任意,开发中一般在classpath下(src) 名称:任意, ...

  2. 跟着大彬读源码 - Redis 2 - 服务器如何响应客户端请求?(上)

    上次我们通过问题"启动服务器,程序都干了什么?",跟着源码,深入了解了 Redis 服务器的启动过程. 既然启动了 Redis 服务器,那我们就要连上 Redis 服务干些事情.这 ...

  3. SpringBoot系列——Logback日志,输出到文件以及实时输出到web页面

    前言 SpringBoot对所有内部日志使用通用日志记录,但保留底层日志实现.为Java Util Logging.Log4J2和Logback提供了默认配置.在不同的情况下,日志记录器都预先配置为使 ...

  4. 该如何真正进入SEO行业?

    今天一个多年的朋友突然问我这个问题,他作为一个seo局外人,感觉SEO挺神秘,我认为要入行就要先了解一个SEO是什么职业,它的工作有那些,然后再考虑怎样进行学习或培训. 一.查看网站状态 seo人员每 ...

  5. Numpy之数组创建

    ndarray 数组除了可以使用 ndarray 构造器来创建外,也可以通过如下方式创建. 一.创建数组 numpy.empty 语法: numpy.empty(shape, dtype = floa ...

  6. VS2008 专业版试用到期破解 【转】

    对于在win7内核下的vs2008破解,和在xp内核系统下的破解是不同的.传统(XP)的破解方式: 一.先安装试用版,然后在“添加或删除程序”里找到VS2008,点“更改/删除”就会看到一个输入序列号 ...

  7. 关于关闭WPS锁屏屏保及设置电脑自动关闭显示屏及休眠的分享

    最近公司工作的电脑突然自动加上了屏保锁屏,百思不得其解什么时候设置的,谁给设置的,未经用户允许就擅自给用户设置了??? 金山WPS未经用户允许给用户设置了锁屏屏保,而且这个功能非常不好用,按键盘有时候 ...

  8. H5学习笔记-应用缓存,Web worker,服务器发送事件

    ↑亮了 应用缓存用法 <!DOCTYPE HTML> <html manifest="demo.appcache"> <body> The co ...

  9. Android 装逼技术之暗码启动应用

    什么是暗码? 在拨号盘中输入*#*#<code>#*#*后,APP 可以监控到这些输入,然后做相应的动作,比如启动应用,是不是有点骚. 下面看下这个骚操作是如何实现的. 效果预览 源码 D ...

  10. Redis图形化客户端管理软件推荐

    Redis是一个超精简的基于内存的键值对NOSQL数据库(key-value),一般对并发有一定要求的应用都用其储存session,乃至整个数据库.不过它公自带一个最小化的命令行式的数据库管理工具re ...