最近工作中遇到了需要使用微信jssdk的场景,捣鼓了大半个礼拜,踩了很多坑,终于还是解决了,记录一下!
官方文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
一.绑定域名
在公众号平台上注册一个测试号,使用natapp进行内网穿透,并配置JS接口安全域名
二.获取access_token
后台部分使用nodejs编写,access_token的有效期目前为2个小时。官方建议开发者使用中控服务器统一获取和刷新access_token,其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,否则容易造成冲突。即2小时内可以使用同一个access_token,不用多次请求,node部分代码如下:
// 定义刷新access_token的函数
function refreshAccessToken() {
const tokenUrl = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${AppID}&secret=${AppSecret}`;
request.get(tokenUrl, function (err, res, body) {
if (err) {
console.error(err);
return;
}
const accessToken = JSON.parse(body).access_token;
// 更新全局变量中的accessToken
global.access_token = accessToken;
//更新accessToken后重新获取jsapiTicket
refreshJsapiTicket()
// 每隔REFRESH_INTERVAL时间间隔刷新一次access_token
setInterval(refreshAccessToken, REFRESH_INTERVAL);
});
}
三.获取jsapi_ticket
和access_token一样,有效期为2个小时,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。node部分代码如下:
// 定义刷新jsapi_ticket的函数
function refreshJsapiTicket() {
const ticketUrl = `https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${global.access_token}&type=jsapi`;
request.get(ticketUrl, function (err, res, body) {
if (err) {
console.error(err);
return;
}
console.log(JSON.parse(body))
const jsapiTicket = JSON.parse(body).ticket;
// 更新全局变量中的jsapiTicket
global.jsapiTicket = jsapiTicket;
});
}
四.url部分
这也是最容易踩坑的地方。按照官方说法,前台按照encodeURIComponent(window.location.href.split('#')[0])传给后台即可,需要注意的是。前台进行了编码,后台一定要对url进行解码,否则会失败。
let url = decodeURIComponent(req.body.url)
五.signature
直接按照官方给的demo,获取nonceStr,timestamp,最后把jsapi_ticket和url组合一下,进行加密,代码如下:
var createNonceStr = function () {
return Math.random().toString(36).substring(2, 15);
};
var createTimestamp = function () {
return parseInt(new Date().getTime() / 1000) + '';
};
var raw = function (args) {
var keys = Object.keys(args);
keys = keys.sort()
var newArgs = {};
keys.forEach(function (key) {
newArgs[key.toLowerCase()] = args[key];
});
var string = '';
for (var k in newArgs) {
string += '&' + k + '=' + newArgs[k];
}
string = string.substr(1);
return string;
};
/**
* @synopsis 签名算法
*
* @param jsapi_ticket 用于签名的 jsapi_ticket
* @param url 用于签名的 url ,注意必须动态获取,不能 hardcode
*
* @returns
*/
var sign = function (jsapi_ticket, url) {
var ret = {
jsapi_ticket: jsapi_ticket,
noncestr: createNonceStr(),
timestamp: createTimestamp(),
url: url
};
var string = raw(ret);
jsSHA = require('jssha');
shaObj = new jsSHA(string, 'TEXT');
ret.signature = shaObj.getHash('SHA-1', 'HEX');
console.log(ret)
return ret;
};
module.exports = sign;
六.写个接口给前台调用
router.post('/check', function (req, res) {
let url = decodeURIComponent(req.body.url)
console.log('url',url)
res.send(sign(global.jsapiTicket, url))
})
七.invalid signature
基本上报错都是报的这个,看了很久的社区,总结了一下,可以从以下几个方面入手排查:
1.先保证签名算法正确,校验工具https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign
2.在签名正确的前提下,大概率就是url出问题了。注意以下几个点:传给后台的是否是encodeURIComponent(window.location.href.split('#')[0]);后台是否进行decode解码;配置的安全接口域名是否和页面url一致;url一定要动态获取,每次url变化的时候需要重新进行校验。