我想停止使用 axios 拦截器时出现的无限循环。 当用户登录时,令牌被设置在本地存储中。 为了测试令牌是否处理良好,我更改了刷新令牌的值,并从本地存储中删除了访问令牌。 当我这样做时,用户应该被重定向到登录页面,但最终陷入无限循环,未经授权的 401 错误。
import axios from "axios";
import { logout } from "../stores/actions/authActions";
import store from "../stores";
import userEnv from "userEnv";
import { REFRESH_TOKEN_ENDPOINT } from "../constants/apiUrlConst";
import { ACCESS_TOKEN_KEY, REFRESH_TOKEN_KEY } from "../constants/tokenConst";
const axiosInstance = axios.create({
baseURL: userEnv.apiUrl,
});
const refreshAccessToken = async () => {
const refresh_token = localStorage.getItem(REFRESH_TOKEN_KEY);
if (!refresh_token) {
store.dispatch(logout());
localStorage.clear();
window.location.href = "/";
} else {
try {
const response = await axiosInstance.get(REFRESH_TOKEN_ENDPOINT);
const access_token = response.data.access_token;
const new_refresh_token = response.data.refresh_token;
localStorage.setItem(ACCESS_TOKEN_KEY, access_token);
localStorage.setItem(REFRESH_TOKEN_KEY, new_refresh_token);
return access_token;
} catch (error) {
return Promise.reject(error);
}
}
};
axiosInstance.interceptors.request.use(
async (config) => {
const url = config.url.toLowerCase();
const method = config.method.toLowerCase();
const token =
url === REFRESH_TOKEN_ENDPOINT && method === "get"
? localStorage.getItem(REFRESH_TOKEN_KEY)
: localStorage.getItem(ACCESS_TOKEN_KEY);
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
// 401 Unauthorized
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
await refreshAccessToken();
return axiosInstance(originalRequest);
} catch (error) {
return Promise.reject(error);
}
}
return Promise.reject(error);
}
);
export default axiosInstance;
任何人都可以帮我找到导致此循环的原因,以及如何阻止它吗?
一种可能的解决方案是添加一个布尔标志来跟踪刷新令牌是否已被刷新。这将防止拦截器在请求失败时重复重试请求。
const alreadyRefreshed = originalRequest._alreadyRefreshed;
if (error.response?.status === 401 && !alreadyRefreshed) {
originalRequest._alreadyRefreshed = true;
通过检查此标志,拦截器将仅尝试刷新令牌一次,从而防止无限循环。
如果上述方法不起作用,您可以尝试在拦截器中添加对
error.response.data
的检查。由于刷新令牌可能会过期或变得无效,因此 401
错误可能并不总是由需要刷新令牌引起。通过检查错误响应,可以确定 401
错误是否是由于刷新令牌无效或过期造成的,然后才尝试刷新它。
axiosInstance.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
const originalRequest = error.config;
const alreadyRefreshed = originalRequest._alreadyRefreshed;
const isInvalidRefreshToken = error.response?.data?.error_description?.includes("Invalid refresh token");
if (error.response?.status === 401 && !alreadyRefreshed && isInvalidRefreshToken ) {
originalRequest._alreadyRefreshed = true;
try {
await refreshAccessToken();
return axiosInstance(originalRequest);
} catch (error) {
return Promise.reject(error);
}
}
return Promise.reject(error);
}
);
防止拦截器不断尝试刷新令牌以解决其他类型的
401
错误,例如过期的访问令牌。