React中如何检测access token过期并使用refresh token处理401错误?
Access Token过期自动刷新实现方案
实现思路
通过axios的拦截器统一处理所有请求的token携带和异常响应:
- 请求拦截器:每次发起请求前读取最新的access token添加到请求头,避免使用固定配置导致token更新后不生效
- 响应拦截器:捕获401过期错误,使用refresh token换取新的access token后自动重试失败的请求,同时加锁避免多个请求同时触发token刷新逻辑
完整代码实现
1. 封装axios公共实例与拦截器
创建单独的api配置文件,比如src/utils/request.js:
import axios from 'axios'; // 替换为你的接口基础域名 const BASE_URL = '你的后端接口基础地址'; // 创建自定义axios实例,统一配置通用参数 const api = axios.create({ baseURL: BASE_URL, headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' } }); // 请求拦截器:自动携带最新的access token api.interceptors.request.use( (config) => { const accessToken = localStorage.getItem('accessToken'); if (accessToken) { config.headers.Authorization = `Bearer ${accessToken}`; } return config; }, (error) => Promise.reject(error) ); // 刷新token锁,避免重复调用刷新接口 let isRefreshing = false; // 存储刷新token期间的待重试请求队列 let retryRequestQueue = []; // 响应拦截器:处理token过期逻辑 api.interceptors.response.use( // 请求成功直接返回响应 (response) => response, async (error) => { const originalRequest = error.config; // 判断是否为access token过期的401错误,且未重试过 if ( error.response?.status === 401 && error.response.data?.message === 'jwt expired' && !originalRequest._retry ) { // 已经在刷新token,将当前请求加入队列等待刷新完成后重试 if (isRefreshing) { return new Promise((resolve) => { retryRequestQueue.push((newAccessToken) => { originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; resolve(api(originalRequest)); }); }); } // 标记请求已重试,开启刷新锁 originalRequest._retry = true; isRefreshing = true; try { const refreshToken = localStorage.getItem('refreshToken'); // 调用后端刷新token接口,替换为你的实际接口地址和参数格式 const refreshRes = await axios.post(`${BASE_URL}/refresh-token`, { refreshToken }); const { accessToken: newAccessToken, refreshToken: newRefreshToken } = refreshRes.data; // 更新本地存储的token localStorage.setItem('accessToken', newAccessToken); localStorage.setItem('refreshToken', newRefreshToken); // 执行队列中所有待重试的请求 retryRequestQueue.forEach((cb) => cb(newAccessToken)); retryRequestQueue = []; // 重试当前失败的请求 originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; return api(originalRequest); } catch (refreshErr) { // refresh token也过期,清空本地存储跳转登录页 localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); window.location.href = '/login'; return Promise.reject(refreshErr); } finally { // 重置刷新锁 isRefreshing = false; } } // 非token过期的错误直接抛出 return Promise.reject(error); } ); export default api;
2. 业务请求改写
原有业务代码直接引入封装好的api实例调用即可,不需要单独配置请求头:
import api from 'src/utils/request'; const getAllProducts = async () => { const response = await api.get('/getProducts'); // 处理返回数据 return response.data; };
注意事项
- 请根据后端实际规则调整刷新token的接口地址、请求参数、返回字段,以及401错误的判断逻辑
- 原有代码中
${url}/ getProducts的路径多了多余空格,需修正为正确的接口路径 - 若项目使用React Router路由,可以替换
window.location.href的跳转逻辑为路由跳转方法,体验更友好
内容的提问来源于stack exchange,提问作者ASIF KAIF
相关产品推荐
相关产品推荐

