这里有几个点是要注意的:html
引用Yinjie 的图,由于这个图比官方的要看得明白一点。git
引用Yinjie 的图,由于这个图比官方的要看得明白一点。es6
引用腾讯 weapp-session 的图github
他们还出了一个比较详细的,一步步的代码处理流程,能够对比本身的程序进行检查。
request
weapp-session-client 包装 request
redis
首次请求算法
wx.login()
和 wx.getUserInfo()
接口得到 code
、rawData
和 signature
requset
的头部带上 code
、rawData
和 signature
code
供下次调用非首次请求数据库
request
的头部带上保存的 code
服务器收到请求 request
,中间件从头部提取 code
、rawData
和 signature
字段小程序
code
为空,跳到第 4
步若是 code
不为空,且 rawData
不为空,须要进行签名校验微信小程序
使用 code
,appid
、app_secret
请求微信接口得到 session_key
和 openid
api
ERR_SESSION_KEY_EXCHANGE_FAILED
rawData
和 session_key
计算签名 signature2
对比 signature
和 signature2
签名一致,解析 rawData
为 wxUserInfo
openid
写入到 wxUserInfo
(code, wxUserInfo)
缓存到 RediswxUserInfo
存放在 request.$wxUserInfo
里4
步ERR_UNTRUSTED_RAW_DATA
若是 code
不为空,但 rawData
为空,从 Redis 根据 code
查询缓存的用户信息
request.$wxUserInfo
字段里,跳到第 4
步ERR_SESSION_EXPIRED
request
被业务处理,可使用 request.$wxUserInfo
来获取用户信息(request.$wxUserInfo
可能为空,业务须要自行处理)code
是微信用户的登陆凭证,打开小程序登陆的时候获取的只属于微信这个用户的登陆凭证,须要注意的是,这个登陆凭证只供微信小程序使用的。session_key
是微信用户在小程序里面的登陆态信息,至关因而微信给这个用户颁发的一个登陆 session,官网地址
{"session_key":"...","expires_in":7200,"openid":"..."}
,须要按期使用wx.checkSession检测。openId
,用户的惟一标识unioinId
,若是开发者拥有多个移动应用、网站应用、和公众账号(包括小程序),可经过unionid
来区分用户的惟一性,由于只要是同一个微信开放平台账号下的移动应用、网站应用和公众账号(包括小程序),用户的unionid
是惟一的。换句话说,同一用户,对同一个微信开放平台下的不一样应用,unionid
是相同的。openId
就是微信用户的惟一标识,可是由于微信产品不少,因此会出现多个微信产品使用不一样的 openId
来标识用户,可是对于咱们作业务接入的话,就买办法使用了,因此建议是统一使用 unioinid
,由于通常来讲,通常的业务都会有公众号,小程序联合使用的。3rd_session
是通常是指咱们本身公司的服务器的 session
,通常来讲,能够跟原来的业务的 session
一块儿使用,不过这个 session
的过时时间必定要比小程序的session_key
的过时时间要长,这样能够减小 session
的屡次重复建立,另一般咱们本身公司的服务器的 session
管理都会使用相似 redis 之类的数据库进行管理的,这个大体了解一下,由于其余文章会提到。session
(session_key
和3rd_session
),那是由于session_key
是微信的登陆态,3rd_session
是咱们业务系统的登陆态,两边各有一个登陆态,因此须要将2个登陆态合并为一个 session
,在这里面是合并为3rd_session
,并保存到咱们业务系统上,而后每次须要使用的时候,小程序带上这个3rd_session
访问咱们的业务系统,经过处理,能够返回咱们须要 unioinid 和其余 session
信息而不用每次都去获取一个新的session_key
(由于微信有限制code 的使用,要用 code 换 session_key),总的来讲,就是使用3rd_session
来管理小程序的登陆态根据官方文档: ,数据校验是为了提升数据的安全性,咱们须要获取用户的 unioinid,须要调用wx.getUserInfo
接口来获取,可是通常状况下,获取出来的数据是
{ "nickName": "Band", "gender": 1, "language": "zh_CN", "city": "Guangzhou", "province": "Guangdong", "country": "CN", "avatarUrl": "http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0" }
对于须要业务接入的话,没有 unioinid 是没意义的,因此须要根据官方的方式来进行解密,解密后的数据里面有 unioinid 了
{ "openId": "OPENID", "nickName": "NICKNAME", "gender": GENDER, "city": "CITY", "province": "PROVINCE", "country": "COUNTRY", "avatarUrl": "AVATARURL", "unionId": "UNIONID", "watermark": { "appid":"APPID", "timestamp":TIMESTAMP } }
由于小程序的全部网络请求都是异步的,那么异步就会出现不少重的回调的问题,因此改为了 promise,promise 的使用要谨慎注意 resolve 和 return的处理。
例如这样:
const httpRequest = (api, data) => { let serverHost = env.serverHost; return new Promise(function (resolve, reject) { //发起网络请求 wx.request({ url: serverHost + api, data: data, header: { 'content-type': 'application/x-www-form-urlencoded' }, success: function (res) { if (res.data.errno === 0) { // 须要下一步处理的就用 resolve 返回 resolve(res.data); } else { console.log("http fail:api:" + api + "res:" + JSON.stringify(res)); // 须要跳出循环处理的就用 reject reject(res.data); } }, fail: function (res) { console.log("http fail:api:" + api + "res:" + JSON.stringify(res)); // 须要跳出循环处理的就用 reject reject(res); } }) }) };
在多重 promise 的状况下,则须要注意2个地方:
getCode() // 获取 wx code .then(code => { wxCode = code; // 这里getSetting是一个返回的 promise实例,如上面的那个 return getSetting(); // 获取 setting }) .then(res => { if (res.authSetting["scope.userInfo"]) { // 这里getUserInfo是一个返回的 promise实例 return getUserInfo(self); } else { console.log("first auth none:" + JSON.stringify(res)); // 相似 return util.showModal("亲,尚未受权!") .then(res => { return getAuth("userInfo"); }).then(res => { // 检查受权是否正常 return getSetting(); }) .then(res => { if (res.authSetting["scope.userInfo"]) { return getUserInfo(self); } else { return Promise.reject(res); } }); } }) .catch(err=>{ console.log("err"+err); })
微信小程序“受权失败”场景须要优雅处理,提高用户体验,参考这里:能够稍微看到是如何生效的:
经过在wx.getSetting
里面插入一个判断处理,判断没有权限即弹出 modal 提示框:
wx.getSetting({ success: function success(res) { console.log(res.authSetting); var authSetting = res.authSetting; if (authSetting['scope.userInfo'] === false) { wx.showModal({ title: '用户未受权', content: '如需XXXXXXX。', showCancel: false, success: function (res) { if (res.confirm) { console.log('用户点击肯定') wx.openSetting({ success: function success(res) { } }); } } }) } } });
参考引用: