最近在实现refesh token,但是在前端页面请求刷新的时候遇到一个问题。
场景如下:刷新页面时,同时有几个接口在进行请求,由于refesh token的操作放在拦截器中,在接口请求时,判断当前token是否过期,如果token过期之后,进行token刷新操作。可想而知会发生什么问题。几个接口争先恐后的去使用refesh_token换取新的token,跑的快的,成功拿到新的token。跑的慢,只有拿第一个接口用过的refesh_token去刷新,服务器看到递过来的是已经用过了的 refesh_token,只有毫不犹豫的拒绝。
所以就需要一个机制,让换取token的操作依次执行,或者只需要执行一次。让第一次执行刷新token之后,后面接口发现token已经被刷新了,就不在去刷新token。
这时候就可以利用js Promise链式调用的特点去解决这个问题。具体思路就是返回Promise对象,通过调用Promise的then函数,返回需要的内容。具体原理的话,我描述不清楚,因为也是是个前端小菜鸡啊~~~所以有啥错误的地方,还请多多指教和包含。
上代码:
1、拦截器中判断token是否过期,过期就操作刷新
let token = store.getters["account/token"];
let timeNow = Math.round(new Date().getTime() / 1000);
if (!requestUrlInIgnoreList(url) && (!token || token.expires_at - 60 <= timeNow)) {
console.log("[Local]AccessToken expiring, start refresh token...");
let newToken = await localRefreshToken();
if (newToken) {
console.log("[Local]Refresh successful, token is " + JSON.stringify(newToken));
config.headers.Authorization = `Bearer ${newToken.access_token}`;
} else {
message.warning("认证已过期,请重新登录");
}
} else {
if (!requestUrlInIgnoreList(url)) {
config.headers.Authorization = `Bearer ${token.access_token}`;
}
}
2、token刷新类
class localAccount {
refreshTokenPromise = null;
refreshToken = async () => {
if (this.refreshTokenPromise) {
console.log("我是后来的,我也要刷新token...");
return this.refreshTokenPromise.then(() => {
let lastRefeshToken = store.getters["account/token"];
return lastRefeshToken;
});
}
let newToken = undefined;
let oldToken = store.getters["account/token"];
if (!oldToken || !oldToken.refresh_token) {
return newToken;
}
this.refreshTokenPromise = request(POST_REFESH, METHOD.POST, {
Token: oldToken.refresh_token,
})
.then((result) => {
if (result && result.data.code == 0) {
let userToken = {
access_token: result.data.data.accessToken,
refresh_token: result.data.data.refreshToken,
token_type: result.data.data.tokenType,
expires_at: result.data.data.expiresAt,
profile: result.data.data.profile,
};
newToken = userToken;
//设置用户
if (newToken) {
store.commit("account/setToken", newToken);
}
this.refreshTokenPromise = null;
return newToken;
}
})
.catch((err) => {
console.error(err);
});
return this.refreshTokenPromise;
};
}
通过定义一个全局的 refreshTokenPromise 对象,第一次请求时, refreshTokenPromise 对象为空,进行token刷新。由于封装的request本身也是一个异步请求,所以将request赋值给 refreshTokenPromise 。所以当第二个刷新请求过来之后,判断 refreshTokenPromise 是否为空,不为空的话,表示正在请求。使用then方法,返回从store取出第一次请求更新的token。
文章评论