每一个有数据交互的小程序,都会涉及到登录、token 等问题,openid 又是什么呢?怎么使用静默续期,来提升用户体验呢?
小程序登录
登录时序
一切的一切,都要从这么一张小程序登录时序图说起:
通常情况下,我们的小程序都会有业务身份,如何将微信帐号和业务身份关联起来呢?这个时候我们需要上图的步骤:
- 小程序调用
wx.login() 获取临时登录凭证code 。
- 小程序将
code 传到开发者服务器。
- 开发者服务器以
code 换取用户唯一标识openid 和会话密钥session_key 。
- 开发者服务器可绑定微信用户身份
id 和业务用户身份。
- 开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。
相关数据或参数
上面的登录时序中,我们会涉及到一些数据和参数,先来了解下它们都是用来做啥的。
临时登录凭证 code 在小程序中调用wx.login() ,能拿到一个code 作为用户登录凭证(有效期五分钟)。在开发者服务器后台,开发者可使用code 换取openid 和session_key 等信息(code 只能使用一次)。
code 的设计,主要用于防止黑客使用穷举等方式把业务侧个人信息数据全拉走。
AppId 与 AppSecret 为了确保拿code 过来换取身份信息的人就是对应的小程序开发者,到微信服务器的请求要同时带上AppId 和AppSecret 。
session_key 会话密钥session_key 是对用户数据进行加密签名的密钥。为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个密钥。
设计session_key 主要是为了节省流程消耗,如果每次都通过小程序前端wx.login() 生成微信登录凭证code 去微信服务器请求信息,步骤太多会造成整体耗时比较严重。
使用接口wx.checkSession() 可以校验session_key 是否有效。用户越频繁使用小程序,session_key 有效期越长。session_key 失效时,可以通过重新执行登录流程获取有效的session_key 。
openid
openid 是微信用户id ,可以用这个id 来区分不同的微信用户。 微信针对不同的用户在不同的应用下都有唯一的一个openid ,但是要想确定用户是不是同一个用户,就需要靠unionid 来区分。
unionid 如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过unionid 来区分用户的唯一性。同一用户,对同一个微信开放平台下的不同应用,unionid 是相同的。
加锁的登录
在某些情况下,我们或许多个地方会同时触发登录逻辑(如多个接口同时拉取,发现登录态过期的情况)。一般来说,我们会简单地给请求加个锁来解决:
- 使用
isLogining 来标志是否请求中。
- 方法返回 Promise,登录态过期时静默续期后重新发起。
- 使用
sessionId 来记录业务侧的登录态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
|
export const SESSION_KEY = 'sessionId';
let isLogining = false;
export function doLogin() { return new Promise((resolve,reject) => { const session = wx.getStorageSync(SESSION_KEY); if (session) {
resolve(); } else if (isLogining) {
setTimeout(() => { doLogin() .then(res => { resolve(res); }) .catch(err => { reject(err); }); },500); } else { isLogining = true; wx.login({ success: res) => { if (res.code) { const reqData: ILoginRequest = { code: res.code } wx.request({ url: API.login, data: reqData,
success: resp) => { const data = resp.data; isLogining = false;
if (data.return_code === 0) { wx.setStorageSync(SESSION_KEY,data[SESSION_KEY]); resolve(); } else { reject(data.return_msg); } }, fail: err => {
isLogining = false; reject(err); } }); } else {
isLogining = false; reject(); } }, fail: err) => {
isLogining = false; reject(err); } }); } }); }
|
登录态静默续期的实现
checkSession
前面也提到,微信不会把session_key 的有效期告知开发者,因此需要使用接口wx.checkSession() 来校验session_key 是否有效。
这里我们:
isCheckingSession来标志是否查询中。
- 返回 Promise。
- 使用
isSessionFresh 来标志session_key 是否有效。
|
import { doLogin } from "./doLogin";
import { SESSION_KEY } "./doLogin";
let isCheckingSession = let isSessionFresh = false;
function checkSession(): Promise<string> { if (isCheckingSession) { setTimeout(() => { checkSession() .then(if (!isSessionFresh && session) { isCheckingSession = true; wx.checkSession({ success: () => {
isSessionFresh = true; resolve(); },150);">// session_key 已经失效,需要重新执行登录流程
wx.removeStorage({ key: "skey", complete: () => { doLogin() .then(() => { resolve(); }) .catch(err => { reject(err); }); } }); }, complete: () => { isCheckingSession = false; } }); } else { doLogin() .then(res => { resolve(res); }) .catch(err => { reject(err); }); } }); }
|