详解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) } } ) })
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。