详解nodejs微信jssdk后端接口
写过了两个微信的页面,遇到了挺多不会的问题,当时也是自己边查资料,边实践完成了简单的需求,刚好现在有空,把之前的东西整理一遍。
与普通的手机页面不同的是,微信页面提供给你了调用微信APP内置功能的接口,可以实现更复杂的功能。
jssdk的前端使用
- 前端页面调用jssdk首先要通绑定“公众号设置”的“功能设置”里填写“JS接口安全域名”
- 然后在页面中引入http://res.wx.qq.com/open/js/...
- 调用wx.config({...})来验证权限配置
- 然后可根据需要调用微信所提供的接口
后端返回接口
在前端调用时wx.config({...})中需要的参数需要我们自己进行返回
wx.config({
debug:true,//开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId:'',//必填,公众号的唯一标识
timestamp:,//必填,生成签名的时间戳
nonceStr:'',//必填,生成签名的随机串
signature:'',//必填,签名
jsApiList:[]//必填,需要使用的JS接口列表
});
其中timestamp,nonceStr,signature,是需要后端计算返回的。
签名获取方法
签名生成规则如下:参与签名的字段包括noncestr(随机字符串),有效的jsapi_ticket,timestamp(时间戳),url(当前网页的URL,不包含#及其后面部分)。对所有待签名参数按照字段名的ASCII码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL转义。
而其中的jsapi_ticket是通过access_token来获取的,且两者都有过期时间(7200秒)其中jsapi_ticket更是限制了获取次数。所以为了保存两者,使用redis数据库保存在内存中是个很好的选择(可快速读取,并设置过期时间)。
token获取方法:
/**
*获取token
*@return{promise}res值为token
*/
functiongetToken(){
returnredis.getVal('token')//首先读取redis是否存在token
.then(function(res){
if(res===null){//若不存在,则返回savetoken()获取
//console.log('不存在token调用saveToken')
returnsaveToken()
}else{//若存在则直接返回
//console.log('存在token直接返回')
returnres
}
})
.catch(function(err){//捕获错误
console.log(err)
})
}
/**
*从服务端获取token并保存在redis中
*@return{promise}值为token
*/
functionsaveToken(){
returnnewPromise((resolve,reject)=>{
letreqUrl=config.gettoken_url//https://api.weixin.qq.com/cgi-bin/token?
letparams={
grant_type:'client_credential',
appid:config.appid,
secret:config.appsecret
}
letoptions={
method:'get',
url:reqUrl+qs.stringify(params)
}
request(options,function(err,res,body){
if(res){
letbodys=JSON.parse(body)
letexpires=bodys.expires_in
lettoken=bodys.access_token
redis.setKey('token',token,expires)//将token保存到redis
.catch(function(err){
console.log(err)
})
resolve(token)
}else{
reject(err)
}
})
})
}
在配置文件中配置好所需要的appid和appsecret,首先查看redis中是否存在,如果存在就直接返回,没有的话,就调用saveToken去获取并保存在redis中
jsapi_ticket获取方法
同理,jsapi_ticket也采用同样的方式去获取
/**
*获取ticket
*@return{promise}res值为ticket
*/
functiongetJsTicket(){//获取token
returnredis.getVal('ticket')//首先读取redis是否存在ticket
.then(function(res){
if(res===null){//若不存在,则返回saveJsTicket()获取
//console.log('不存在ticket调用saveJsTicket')
returnsaveJsTicket()
}else{//若存在则直接返回
//console.log('存在ticket直接返回')
returnres
}
})
.catch(function(err){//捕获错误
console.log(err)
})
}
/**
*从服务端获取ticket并保存在redis中
*@return{promise}值为ticket
*/
functionsaveJsTicket(){
returnnewPromise((resolve,reject)=>{
getToken().then(function(token){
letreqUrl=config.ticket_start+token+config.ticket_end
letoptions={
method:'get',
url:reqUrl
}
request(options,function(err,res,body){
if(res){
letbodys=JSON.parse(body)//解析微信服务器返回的
letticket=bodys.ticket//获取ticket
letexpires=bodys.expires_in//获取过期时间
redis.setKey('ticket',ticket,expires)//将ticket保存到redis
.catch(function(err){
console.log(err)
})
resolve(ticket)
}else{
reject(err)
}
})
}).catch(function(err){
console.log(err)
})
})
}
签名算法
在获取jsapi_ticket后就可以生成JS-SDK权限验证的签名了
/**
*1.appId必填,公众号的唯一标识
*2.timestamp必填,生成签名的时间戳
*3.nonceStr必填,生成签名的随机串
*4.signature必填,签名
*/
constcrypto=require('crypto')
constgetJsTicket=require('./getJsTicket')
constconfig=require('../../config')//微信设置
//sha1加密
functionsha1(str){
letshasum=crypto.createHash("sha1")
shasum.update(str)
str=shasum.digest("hex")
returnstr
}
/**
*生成签名的时间戳
*@return{字符串}
*/
functioncreateTimestamp(){
returnparseInt(newDate().getTime()/1000)+''
}
/**
*生成签名的随机串
*@return{字符串}
*/
functioncreateNonceStr(){
returnMath.random().toString(36).substr(2,15)
}
/**
*对参数对象进行字典排序
*@param{对象}args签名所需参数对象
*@return{字符串}排序后生成字符串
*/
functionraw(args){
varkeys=Object.keys(args)
keys=keys.sort()
varnewArgs={}
keys.forEach(function(key){
newArgs[key.toLowerCase()]=args[key]
})
varstring=''
for(varkinnewArgs){
string+='&'+k+'='+newArgs[k]
}
string=string.substr(1)
returnstring
}
/**
*@synopsis签名算法
*
*@paramjsapi_ticket用于签名的jsapi_ticket
*@paramurl用于签名的url,注意必须动态获取,不能hardcode
*
*@returns{对象}返回微信jssdk所需参数对象
*/
functionsign(jsapi_ticket,url){
varret={
jsapi_ticket:jsapi_ticket,
nonceStr:createNonceStr(),
timestamp:createTimestamp(),
url:url
}
varstring=raw(ret)
ret.signature=sha1(string)
ret.appId=config.appid
returnret
}
/**
*返回微信jssdk所需参数对象
*@param{字符串}url当前访问URL
*@return{promise}返回promise类val为对象
*/
functionjsSdk(url){
returngetJsTicket()
.then(function(ticket){
returnsign(ticket,url)
})
.catch(function(err){
console.log(err)
})
}
functionrouterSdk(req,res,next){
letclientUrl=req.body.url
if(clientUrl){
jsSdk(clientUrl)
.then(function(obj){
res.json(obj)
})
}else{
res.end('nourl')
}
}
module.exports=routerSdk
以上基本就完成了后端返回签名的过程(省略了redis部分)。具体细节可参考我当时的练手项目中的代码。
至此,前端就可以使用jssdk来完成功能的调用了。
ps:某次使用录音接口做了一个功能,但是发现,微信服务器只会保存3天数据,需要自己下载到自己的服务器才行,不知道诸位有没做过类似的需求,给我提供下指导啥的,感激不尽~
后记
后来又写过一个获取用户信息的页面,感觉也是挺常用的就写个demo出来看看吧(没有做access_token的保存,好像是没有获取次数限制)。
router.get('/',function(req,res,next){
console.log("oauth-login")
//第一步:用户同意授权,获取code
letrouter='get_wx_access_token'
//这是编码后的地址
letreturn_uri=encodeURIComponent(base_url+router)
console.log('回调地址:'+return_uri)
letscope='snsapi_userinfo'
res.redirect('https://open.weixin.qq.com/connect/oauth2/authorize?appid='+appid+'&redirect_uri='+return_uri+'&response_type=code&scope='+scope+'&state=STATE#wechat_redirect')
})
//第二步:通过code换取网页授权access_token
router.get('/get_wx_access_token',function(req,res,next){
console.log("get_wx_access_token")
console.log("code_return:"+req.query.code)
letcode=req.query.code
request.get(
{
url:'https://api.weixin.qq.com/sns/oauth2/access_token?appid='+appid+'&secret='+appsecret+'&code='+code+'&grant_type=authorization_code',
},
function(error,response,body){
if(response.statusCode===200){
//第三步:拉取用户信息(需scope为snsapi_userinfo)
//console.log(JSON.parse(body))
letdata=JSON.parse(body)
letaccess_token=data.access_token
letopenid=data.openid
request.get(
{
url:'https://api.weixin.qq.com/sns/userinfo?access_token='+access_token+'&openid='+openid+'&lang=zh_CN',
},
function(error,response,body){
if(response.statusCode==200){
//第四步:根据获取的用户信息进行对应操作
letuserinfo=JSON.parse(body)
console.log(JSON.parse(body))
console.log('获取微信信息成功!')
小测试,实际应用中,可以由此创建一个帐户
res.send("\
"+userinfo.nickname+"的个人信息
\
\
"+userinfo.city+","+userinfo.province+","+userinfo.country+"
\
")
}else{
console.log(response.statusCode)
}
}
)
}else{
console.log(response.statusCode)
}
}
)
})
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。