token过期后,需要前端携带旧的token去请求后端接口置换新的token
实现思路:协定错误码XXX,在响应拦截中拦截,发起刷新token请求,刷新完毕调用axios实例重新发起中断的请求。
我这里用的是401状态码
//刷新token
case 401:
//发起刷新token的请求
await getToken({}).then((res) =>
if (res.code == 200) {//刷新成功
cookies.set("token", res.data); //替换新的token
service(response.config);//重新发起由于token过期中断的请求
} else {
//刷新失败 重新登录
if (doms2 == undefined) {
Message.error({
message: "登录失效,请重新登录",
});
}
//清除登录状态
cookies.remove("infor");
cookies.remove("token");
}
});
这样做有个问题:当token过期,一个页面异步同时发出多个请求时,多接口都携带旧token去请求,不同时间返回401错误,挨个都去置换token造成被多次刷新异常。
解决思路:
-
草根方案:
使用async、await函数把页面所有异步请求变成同步,等待上一个执行完毕再执行下一个请求。
-
较完美方案:
在响应拦截器中第一个返回401执行刷新token的操作时候,设置一个正在更新token的状态,
其他的异步并发的请求返回401被拦截下来就不要去重复的执行更新token,用函数的形式暂存起来,
等待更新完毕再一起重新发起。
let isRefreshing = false; // 定义一个是否正在刷新token的标记
let retryRequests = [];// 定义一个空数组,承载请求队列
//刷新token
case 401:
//判断是否正在更新token
if (!isRefreshing) {
isRefreshing = true; //更新状态
//发起刷新token
await getToken({}).then((res) => {
if (res.code == 200) {
cookies.set("token", res.data); //刷新成功
//遍历缓存队列 发起请求 传入最新token
retryRequests.forEach((cb) => cb(res.data));
// 重试完清空这个队列
retryRequests = [];
service(response.config);
} else {
//刷新失败 重新登录
if (doms2 == undefined) {
Message.error({
message: "登录失效,请重新登录",
});
}
//清除登录状态
cookies.remove("infor");
cookies.remove("token");
}
});
// 正在刷新token,返回一个未执行resolve的promise
} else {
//这里的Promise里并没有调用resolve()去改变状态,而是直接push一个函数方法到数组
//这样就可以把请求暂存起来,等待上面刷新token请求成功后,遍历 retryRequests数组
//把新的token通过函数传参方式传入 调用方法执行重新请求
return new Promise((resolve) => {
// 将resolve放进队列,用一个函数形式来保存,等token刷新后调用执行
retryRequests.push((token) => {
response.config.headers.Authorization = token;
resolve(service(response.config));
});
});
}
break;
以上已经实现异步请求并发时,暂存错误队列,等待token刷新成功后统一重新发起请求的问题。
有描述不清楚或者错误的地方欢迎提出指正。