我们知道如果没有权限,那么接口返回的状态值是401,请求接口的时候,接口鉴权需要我们在请求头携带token值,而无感知登录的话,一般有access_token和refresh_token,在请求刷新的时候,一般接口会返回access_token和refresh_token值,那么什么情况下才去请求refresh接口呢?这么逻辑状态值就是access_token 401 并且是非刷新接口/refrsh, ok 根据这个条件我们可以写出这样的代码
if(response.status === '401' && !config.url.includes("/refresh")) {
1:请求refresh 接口,重新把access_token和refresh_token的值存到localStoreage去
2: 重写更改config下的请求头的headers下的AutherToken值,最后return axios(config),从而实现无感登录
}
具体代码
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
interface PendingTask {
config: AxiosRequestConfig;
resolve: (value: AxiosResponse | PromiseLike<AxiosResponse>) => void;
reject: (reason?: any) => void;
}
const axioxInstance = axios.create({
baseURL: "http://localhost:3000/api",
timeout: 10000,
headers: {
"Content-Type": "application/json",
},
});
const pendingTasks: PendingTask[] = []; // 存储请求队列
let isRefreshing = false; // 更清晰的命名
// 请求拦截器
axioxInstance.interceptors.request.use((config) => {
// 在这里添加请求拦截器
let tokenKey = localStorage.getItem("access_token") as string;
const token = localStorage.getItem(tokenKey) as string;
if (token) {
config.headers.Authorization = `Bearers ${token}`;
}
return config;
});
// 返回响应拦截器
axioxInstance.interceptors.response.use(
(res) => {
return res.data;
},
async (error) => {
const { config, response } = error;
if (response.status === "401" && !config.url.includes("/refreshToken")) {
// 是否是已经在无刷新token了
if (isRefreshing) {
// 如果是,返回一个新的promise,并且把他推到任务队列去
return new Promise((resolve, reject) => {
pendingTasks.push({
config,
resolve,
reject,
});
});
}
isRefreshing = true; // 设置为正在刷新状态
const res = await refreshToken();
if (res.status === 200) {
// 释放所有等待的请求
pendingTasks.forEach((task) => task.resolve(task.config));
// 清空队列
pendingTasks.length = 0;
config.headers.Authorization = `Bearer ${res.data.access_token}`; // 更新请求头的token值
return axioxInstance(config); // 返回这样的一个config,就是为了实现一个无感的请求,此时所有的请求中的config都已经拿到最新的token值了,就可以正常请求了
}
} else {
// 失败的时候,也要把队列清空,并且要把错误信息返回出去
pendingTasks.forEach((task) => task.reject(error));
pendingTasks.length = 0;
alert("登录过期,请重新登录");
return error.response;
}
}
);
async function refreshToken() {
const res = await axioxInstance.get("/refresh", {
params: {
token: localStorage.getItem("refresh_token"),
},
});
localStorage.setItem("access_token", res.data.access_token);
localStorage.setItem("refresh_token", res.data.refresh_token);
return res;
}