共计 3754 个字符,预计需要花费 10 分钟才能阅读完成。
小程序中实现 token 过期从新登录再从新申请业务接口
首先还是来看下小程序官网给出的小程序登录流程:
- 小程序官网登录地址
- 小程序官网登录流程图
咱们这里所说的 token 就是指的官网说的 自定义登录态
,token 个别都是有时效性,依照此流程,当 token 生效的时候,页面在申请接口的时候服务端必定会认为 token 是非法的,此时就须要小程序端从新获取新的 token,而后持续接着前面的流程走。个别服务端都会给一个特定的状态码标记须要从新获取 token,前面的场景咱们都以服务端返回 code 码 401 认为须要去登录。
场景 1:token 生效之后间接跳转到特定的一个受权登录页面
因为 getUserInfo 接口变动的缘故,必须用户手动去触发一个按钮,个别都会做一个特定的受权登录页面或者弹窗,那此时在接口返回 401 的时候那咱们间接就跳转到那个受权页面或者弹起受权登录弹窗即可,相当于是走了用户第一次进入该小程序的流程。
这种计划是最简略的解决方案,然而毛病也很显著:打断了用户的操作流程,反复了第一次受权登录的流程,很繁琐。
场景 2:token 生效之后无感知获取新 token 并持续之前的操作
分 2 步来解决这个问题,无感知获取 token
这个其实很简略,调 wx.login()获取 code 之后再调服务端新的接口,此接口只须要承受 code 返回以后登录账户最新的信息(token 以及其余),拿到最新 token。
具体代码实现如下:
http.js 文件外围代码
export class Http {constructor() {}
request({url, data = {}, method, header, callback = ''} = {}) {let _this = this; return new Promise((resolve, reject) => {
wx.request({url, data, method, header: { Authorization: 'Bearer' + storge.get(LOGIN_TOKEN) },
callback, fail(res) {reject(res)
},
complete: res => {if (callback) return callback(res.data);
let statusCode = res.statusCode;
if (statusCode == 404) {console.log('接口不存在')
} else if (statusCode == 401) {getNewToken().then(() => {_this.request({ url, data, method, callback: resolve})
}) } else if (statusCode == 200) {resolve(res.data)
} else if (statusCode.startsWith('5')) {wx.showModal({ content: '服务器报错,请重试!', showCancel: false});
}
}
}) }) }}
// 获取 token
const getNewToken = () => {return new Promise((resolve, reject) => {
wx.login({success(res) {
wx.request({
url: '获取最新 token 接口地址',
method: 'POST',
success(res) {
let r = res.data;
// 将所有存储到观察者数组中的申请从新执行。if (r.code == 0) {const token = r['data']['token'];
wx.setStorageSyn('LOGIN_TOKEN', token);
resolve(res);
}
} }) },
fail(err) {reject()
console.error('wx login fail', err);
}
});
})
}
接下来看下如何应用:
api/index.js 文件局部 api
import {Http} from '../utils/http.js';export class Index extends Http {constructor() {super();
}
// 获取商品分类
goodsList(data) {
return this.request({
url: 'goodsList',
method: 'GET',
data: data
});
}
}
pages/index/index.js 文件中调用该接口,局部代码:
import {Index} from '../../api/index.js';
const API = new Index();
Page({
/** * 页面的初始数据
*/ data: {},
// 获取商品列表
getGoodList(page) {
let param = this.data.params;
let params = {page: page, ...param};
return new Promise(resolve => {API.getGoodList(params).then(res => {// 业务逻辑})
}) } ...
})
如此,咱们来试验一番,看到底能够不
如图,看到在调 user 接口的时候发现 token 生效(服务端返回了 401),会调 issue 获取最新的 token, 而后持续走 user 接口,整个过程用户无感知,而且也不会中断之前的操作,是不是很完满,答案是 NO! 往下看
遇到业务简单的页面,会有多个申请,此时,获取最新 token 的这个接口就会屡次调用,这个接口其实只须要调一次就能够了,这样就影响性能了,那如何对这种进行优化呢?其实办法有很多种,最简略的就是利用公布订阅模式,简略批改代码如下:
let isRefreshing = true;let subscribers = [];
function onAccessTokenFetched() {subscribers.forEach((callback) => {callback();
})
subscribers = [];}
function addSubscriber(callback) {subscribers.push(callback)
}
export class Http {constructor() {}
request({url, data = {}, method, header, callback = ''} = {}) {let _this = this; return new Promise((resolve, reject) => {
wx.request({url, data, method, header: { Authorization: 'Bearer' + storge.get(LOGIN_TOKEN) },
callback, fail(res) {reject(res)
},
complete: res => {if (callback) return callback(res.data);
let statusCode = res.statusCode;
if (statusCode == 404) {console.log('接口不存在')
} else if (statusCode == 401) {
// 将须要从新执行的接口缓存到一个队列中
addSubscriber(() => {_this.request({ url, data, method, header, callback: resolve})
}) if (isRefreshing) {getNewToken(url, data).then(() => {
// 顺次去执行缓存的接口
onAccessTokenFetched();
isRefreshing = true; })
} isRefreshing = false;} else if (statusCode == 200) {resolve(res.data)
} else if (statusCode.startsWith('5')) {wx.showModal({ content: '服务器报错,请重试!', showCancel: false});
}
}
}) }) }}
// 获取 token
const getNewToken = () => {return new Promise((resolve, reject) => {
wx.login({success(res) {
wx.request({
url: '获取最新 token 接口地址',
method: 'POST',
success(res) {
let r = res.data;
// 将所有存储到观察者数组中的申请从新执行。if (r.code == 0) {const token = r['data']['token'];
wx.setStorageSyn('LOGIN_TOKEN', token);
resolve(res);
}
} }) },
fail(err) {reject()
console.error('wx login fail', err);
}
});
})
}
对,就是这么简答,把所有 401 的接口执行缓存到一个队列中,拿到最新 token(这里应用了申请锁,只执行一次获取 token 的接口),接着再顺次去走之前的逻辑,整个过程无感知,而且页面不论有多少个申请,始终只会调一次获取 token 的接口。上面来简略测试下:
看到,issue 接口只调取了一次,如此就能够完满的解决小程序中 token 过期从新无感知登录并从新申请之前所有的接口。