背景
最近项目用户登录页面处于安全考虑,登录之后的access_token
并非永久有效而是有实效性了,因此
需要在用户登录之后token
失效的情况下进行access_token
的无感知刷新,也既是需要前端在access_token
失效时调用一次refresh API
使得access_token
再次有效,而不影响用户的使用体验。
需求拆解
用户登录成功之后API
会将{ access_token: '', refresh_token: '', expired: '' }
返回,那就可以在用户发起一个请求时,判断access_token
是否过期,过期就刷新access_token
。首先判断access_token
过期可以在请求之前根据expired
判断,也可以在请求结果回来之后判断,问题的难点其实是指如何在多个请求进来时将这些请求暂存下来,然后在refresh
之后在做请求场景还原。
实现思路
针对需求拆解主要需要解决两个问题,一是失效判断,二是多个请求还原,具体解法如下:
- 失效判断:(1)请求前根据
expired
的过期时间进行判断,但这种操作是不可靠的,因为时间是可修改的,所以不采用该方法;(2)根据请求结果判断access_token
过期,然后进行刷新操作,然后在进行一次请求。 - 多请求的场景还原:当同时有多个过期请求进来时,需要避免多次
refresh
的请求,设置一个标志位,当已经有refresh
的操作在进行时,再进来的请求进行挂起(此时可以利用Promise
的pending
状态做文章),等待refresh
成功之后在进行请求。
伪代码实现如下
import axios, { AxiosRequestConfig } from 'axios';
interface IRequestConfig {
tokenInfoKey: string; // 由于登录之后的 token 数据时存在 localstorage 需要键值
onRefreshError: VoidFunction;
}
interface ITokenInfo {
access_token: string;
refresh_token: string;
expired: string;
}
class Request {
private config: IRequestConfig = {};
private tokenInfo: ITokenInfo | null = null;
private isRefreshing: boolean = false;
private needRetryRequest = [];
constructor (config: IRequestConfig) {
const { tokenInfoKey } = config;
this.tokenInfo = JSON.parse(localStorage.getItem(tokenInfoKey));
this.config = config;
}
refreshAccessToken (onSuccess: VoidFunction) {
return axios.request<ITokenInfo>({
url: '',
method: '',
})
.then((res) => {
localStorage.setItem(this.config.tokenInfoKey, res.data);
onSuccess();
})
.catch(() => {
this.needRetryRequest = [];
});
}
request (config: AxiosRequestConfig) {
if (this.isRefreshing) {
return new Promise((resolve) => {
this.needRetryRequest.push(async () => await resolve(axios));
});
}
return axios
.request(config)
.then(res => {
return res.data;
})
.catch(e => {
// 假设 401401 定义需要 refresh
const code = 401401;
const { status } = e.response;
if (status === code) {
this.isRefreshing = true;
this.refreshAccessToken(() => {
Promise.all([() => this.request(config), ...this.needRetryRequest].map(cb => cb()));
});
}
})
.finally(() => {
this.isRefreshing = false;
});
}
}
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!