设计思想:
1.login接口返回两个token,token和refresh token。将两个token缓存在本地
2.当接口返回401时,token过期,需要拿refresh token去换取新的token。将新的token替换旧的token。
3.多个接口返回401时,refresh token接口只需要发送一次,避免多次发送refresh token的请求。
4.将页面请求接口保存起来,当refresh token接口请求成功后,重新发送页面请求。
5.当refresh token也过期时,重新登录。
import axios from "axios";
const service = axios.create({
baseURL: "https://api.example.com",
});
const requestsQueue = [];
// 请求拦截器
service.interceptors.request.use(
(config) => {
const token = localStorage.getItem("token");
if (token) {
config.headers["Authorization"] = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
(response) => {
return response;
},
(error) => {
let { config, data } = error.response;
// 判断Token是否过期
if (data && data.status === 401 && !isRefreshRequest(config)) {
//如果是refreshToken也失效了,就不发送刷新token的接口。
//保存页面请求接口
requestsQueue.push(config);
// Token过期,尝试刷新Token
return refreshToken()
.then((newToken) => {
// 存储新的Token
localStorage.setItem("token", newToken);
// 使用新的Token重新发送之前的请求
config.headers["Authorization"] = `Bearer ${newToken}`;
retryRequestQueue(newToken);
return service(config);
})
.catch(() => {
alert("登录过期,请重新登录");
});
}
return Promise.reject(error);
}
);
let refreshTokenPromise = null;
function refreshToken() {
if (refreshTokenPromise) {
// 如果已经有一个刷新请求正在进行,直接返回该Promise
return refreshTokenPromise;
}
refreshTokenPromise = new Promise((resolve, reject) => {
axios
.get("/auth/refresh", {
headers: {
Authorization: `Bearer ${localStorage.getItem("refreshToken")}`,
},
__isRefreshToken: true,
})
.then((response) => {
// 存储新的Token
localStorage.setItem("token", response.data.accessToken);
localStorage.setItem("refreshToken", response.data.refreshToken);
resolve(response.data.accessToken);
})
.catch((error) => {
reject(error);
});
});
refreshTokenPromise.finally(() => {
refreshTokenPromise = null;
});
return refreshTokenPromise;
}
function isRefreshRequest(config) {
return !!config.__isRefreshToken;
}
function retryRequestQueue(newToken) {
requestsQueue.forEach((config) => {
config.headers["Authorization"] = `Bearer ${newToken}`;
axios(config);
});
requestsQueue.length = 0; // 清空队列
}