React Native中使用Axios如何在JWT过期刷新后重发原请求
Axios 刷新JWT后重发原请求的实现方案
核心逻辑说明
你已经完成的token过期校验、refreshToken调用逻辑都可以和Axios的拦截器机制结合,重发原请求的核心是复用请求拦截器/响应拦截器中拿到的原始请求配置对象,更新Authorization头后直接用Axios实例重新调用该配置即可。
同时需要加刷新状态锁和请求队列,避免并发场景下重复调用refreshToken接口。
完整代码实现
前置变量声明
// 标记当前是否正在刷新token,避免重复发起刷新请求 let isRefreshing = false // 存储刷新token过程中积压的请求回调队列 let requestQueue = []
1. 适配你已有的「请求前校验token过期」逻辑的方案
你可以直接在请求拦截器中完成过期校验+刷新+重发的全流程:
import axios from 'axios' // 替换为你自己项目里的token存取、校验工具,比如用@react-native-async-storage/async-storage做存储 import { getAccessToken, getRefreshToken, saveAccessToken, isTokenExpired, clearToken } from './authUtils' // 替换为你自己的路由实例,用于refresh失败时跳登录 import navigation from './navigationRef' // 初始化自定义Axios实例 const apiClient = axios.create({ baseURL: '你的接口根地址', timeout: 15000 }) // 请求拦截器 apiClient.interceptors.request.use( async (config) => { const accessToken = getAccessToken() // 统一给请求加Authorization头 if (accessToken) { config.headers.Authorization = `Bearer ${accessToken}` } // 你已有的token过期校验逻辑 const expired = isTokenExpired(accessToken) if (!expired) return config // 走到这里说明token已过期 if (!isRefreshing) { isRefreshing = true try { // 调用你已有的refreshToken接口,传入refreshToken拿新的accessToken const newAccessToken = await refreshTokenRequest(getRefreshToken()) // 保存新token到本地 saveAccessToken(newAccessToken) // 更新当前请求的头信息 config.headers.Authorization = `Bearer ${newAccessToken}` // 执行队列中积压的所有请求,传入新token requestQueue.forEach(cb => cb(newAccessToken)) requestQueue = [] // 直接返回更新后的配置,Axios会自动重发当前请求 return config } catch (err) { // refreshToken也过期/失效,清空本地token跳登录页 clearToken() navigation.navigate('Login') return Promise.reject(err) } finally { isRefreshing = false } } else { // 已经在刷新token了,把当前请求加入队列,等刷新完成后再执行 return new Promise(resolve => { requestQueue.push(newToken => { config.headers.Authorization = `Bearer ${newToken}` resolve(config) }) }) } }, error => Promise.reject(error) ) export default apiClient
2. 补充:响应拦截器捕获401错误的适配方案
如果存在token未到过期时间但提前失效(比如被后端吊销、篡改)的场景,可以加响应拦截器兜底,核心重发逻辑如下:
apiClient.interceptors.response.use( res => res, async error => { const originalRequest = error.config // 仅处理401错误、且不是重发的请求,避免死循环 if (error.response?.status === 401 && !originalRequest._retry) { if (isRefreshing) { // 刷新中,把请求加入队列 return new Promise(resolve => { requestQueue.push(newToken => { originalRequest.headers.Authorization = `Bearer ${newToken}` originalRequest._retry = true resolve(apiClient(originalRequest)) }) }) } originalRequest._retry = true isRefreshing = true try { const newAccessToken = await refreshTokenRequest(getRefreshToken()) saveAccessToken(newAccessToken) originalRequest.headers.Authorization = `Bearer ${newAccessToken}` // 执行队列请求 requestQueue.forEach(cb => cb(newAccessToken)) requestQueue = [] // 核心重发逻辑:传入原请求配置重新调用Axios实例 return apiClient(originalRequest) } catch (err) { clearToken() navigation.navigate('Login') return Promise.reject(err) } finally { isRefreshing = false } } return Promise.reject(error) } )
注意事项
- 给原请求配置加
_retry标记是为了避免重发的请求再次401时进入无限循环 - 请求队列可以解决同时发起多个请求时,重复调用refreshToken接口的问题
- React Native环境下注意处理refreshToken调用时的网络错误、本地存储读写异常的边界情况
内容的提问来源于stack exchange,提问作者Amin
相关产品推荐
相关产品推荐

