前言
从本篇开始,我将会将我对于小程序相关优化的经验写成一个系列的文章,希望能够对大家有帮助,创作不易,希望大家多多点赞收藏!!
为什么要做无感刷新
起因是我们小程序收到用户的反馈说经常要刷新~~
都说顾客是上帝,那针对这个问题我们应该怎么去做优化呢?这就是我们本篇文章要介绍的无感刷新
什么是无感刷新
常规的token认证流程
在介绍无感刷新之前,我们先回顾一下常规的token认证流程:
我们一般的流程就是
- 登录的时候获取有效的token
- 将token保存到本地
- 请求其他接口时,通过拦截器在header里面携带上token
- 服务端校验token是否有效,如果有效则直接返回正确的响应,如果过期则返回状态码401
- 前端如果发现状态码是401则证明用户token已过期,强制让用户返回登录页重新登录
那这个流程会有什么问题呢?就是token会有一个过期时间,而一般出于安全考虑token的过期时间不会设置得很长,这就有一个问题就是这个时间是一个绝对时间,就是无论如何时间一到就会立即失效,这样的体验就会不是很好。因此无感刷新token就应运而生~~
无感刷新
无感刷新听起来很高大上,实际上它的原理非常简单:
在上面普通的流程的第一步,我们登录成功后要返回两个token, 一个access_token,一个refresh_token。access_token的有效期较短,而refresh_token的有效期则需要长一点。然后当我们发现access_token过期后,我们就不能像普通流程一样强制用户重新登录了,而是应该拿refresh_token去尝试更新我们的access_token。
流程图如下:
实现
在动手写代码之前,我们先不要着急,我们先要整理一下我们需要改动的地方
- 登录模块: 我们需要将access_token和refresh_token两个token都保存到本地
- 请求模块: 当发现请求返回状态码401,我们需要调用refreshToken接口去换token
- 退出模块: access_token和refresh_token都需要清空
上面的登录模块和退出模块都比较简单,我们就不过多叙述了,我们将目光聚集在请求模块的实现。
我这里用的是UniApp, 原生小程序其实也是一样的,最重要的是意会~~
let requestQueue: UniApp.RequestOptions[] = []; // 请求队列-用于记录
else if (res.statusCode === 401) {
// 401错误 -> 尝试刷新token, 如果刷新失败,返回登录页重新登录
const refreshToken: string = getLocalToken("refreshToken");
// 如果不存在refreshToken,则直接返回登录页
if(!refreshToken) {
goLogin();
reject(res);
} else {
// 将不是刷新token的请求保存到队列中
if(options.url.indexOf("/refreshToken") < 0) {
requestQueue.push(options);
}
// 刷新token
refreshUserToken(refreshToken).then(res => {
const { access_token, refresh_token } = res.data;
// 保存token
setToken(access_token, "token");
setToken(refresh_token, "refreshToken");
// 将缓存队列清空
while (requestQueue.length > 0) {
const curTaskOptions = requestQueue.pop();
if (curTaskOptions && curTaskOptions.url.indexOf("/staffAuth/refreshToken") < 0) {
http(curTaskOptions).then(res => {
resolve(res as Data<T>);
});
}
}
}).catch(() => {
goLogin();
reject(res);
});
}
}
// 返回首页
const goLogin = () => {
uni.showToast({
icon: "none",
title: "Token已过期,请重新登录"
}).then(() => {
clearToken(); // 清空token
requestQueue = []; // 清空请求队列
setTimeout(() => {
uni.navigateTo({ url: "/pages/login/index" });
}, 1000);
});
};
上面的实现有个关键就是,我们需要把所有401的请求都存到一个队列中,然后当我们刷新完token之后,我们就要将队列中的请求一个一个拿出来重新执行。