需求:最近项目有个需求,后台返回token之后,过一段时间就会过期,为了让用户无感知操作,前端需要利用旧token 去请求新token,请求成功之后,用新token 接着进行接口调取。
关于刷新token的问题,网上查了很多,有很多解决方案,要和后台的同学商量好,总之目的就是替换token,并且让用户无感操作。
我的思路为利用axios 的拦截器,判断如果返回是401 ,那么就利用旧token去请求新token,完成之后再继续请求之前未成功的接口和 缓冲起来的接口(见下文)。
问题一:token 过期之后返回401 ,利用旧token 获取到新 token 之后,覆盖本地token,如何接着调取前面失败到接口。解决办法为
axios.request(config); 注意需要return ,见下文,这样就会重新走一遍axios 请求
问题二:在 return axios.request(config) 之前,注意需要在config 里面把token 替换掉,因为config 里面的token是之前过期的。如有其余需要替换的,按实际情况来做。类似如下:
error.config.headers.Authorization = res.data.Data; (res.data.data 为新返回的token)
问题三:如果token 过期后,刚好需要请求多个接口,请求接口为异步,此时就会出现多次调取刷新token的接口。当第一次调取刷新token接口时,还没有返回新token,此时,第二个接口又用过期token请求,导致会出现多吃请求的问题解决办法为设置一个变量,判断是否刷新,如果正在刷新,就把这个接口缓冲起来,等到请求成功之后,再遍历缓冲起来的接口,重新请求。
问题四:缓冲起来的接口,遍历请求的时候,这里也会有异步的问题,我们用promise 来解决。
完整代码如下:
// 是否正在刷新
let isRefreshing = false;
// 重试队列 每一项都是一个待执行待函数
let requests: any[] = [];
// 401: token 过期 请求新token ,
case 401:
if (!isRefreshing) {
isRefreshing = true;
// 获取旧 token
let token = sessionStorage.getItem('token');
// 刷新token
axios.post(`${base.url}System/RefreshToken?id=${token}`).then(res => {
// 重新设置token
sessionStorage.setItem('token', res.data.Data);
// 重新请求接口 前过期的接口
error.config.headers.Authorization = res.data.Data;
error.config.headers.withCredentials = true;
requests.length > 0 && requests.map((cb) => {
cb();
});
requests = []; //注意要清空
return axios.request(error.config);
}).catch(err => {
Message('刷新token失败,请重新登录');
toLogin();
}).finally(() => {
isRefreshing = false;
})
} else {
// 正在刷新token ,把后来的接口缓冲起来
return new Promise((resolve) => {
requests.push(() => {
error.config.headers.Authorization = sessionStorage.getItem('token');
error.config.headers.withCredentials = true;
resolve(axios.request(error.config));
});
})
}
break;