React+RTK Query实现JWT Token自动刷新:自动重试失败请求
RTK Query全局自动刷新JWT令牌方案
你完全可以通过自定义RTK Query的baseQuery实现全局403拦截与令牌自动刷新,彻底消除重复代码。核心思路是在请求的基础逻辑中统一处理错误,遇到403时自动调用刷新接口、重试原请求,同时避免并发请求重复触发刷新。
步骤1:定义刷新令牌的Mutation
先确保你有一个用于刷新令牌的RTK Query Mutation,逻辑是调用/refresh端点并获取新的访问令牌:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; export const authApi = createApi({ reducerPath: 'authApi', baseQuery: fetchBaseQuery({ baseUrl: '/api', credentials: 'include' // 确保携带存储刷新令牌的Cookie }), endpoints: (builder) => ({ refreshTokens: builder.mutation({ query: () => ({ url: '/refresh', method: 'POST', }), }), }), }); export const { useRefreshTokensMutation } = authApi;
步骤2:实现带重鉴权的自定义BaseQuery
创建一个封装默认fetchBaseQuery的自定义baseQuery,在这里统一处理403错误:
import { fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { authApi } from './authApi'; // 全局锁:避免并发请求重复触发刷新 let refreshTokenPromise = null; // 基础请求逻辑 const baseQuery = fetchBaseQuery({ baseUrl: '/api', prepareHeaders: (headers, { getState }) => { // 从Redux Store读取当前访问令牌,添加到请求头 const accessToken = getState().auth.accessToken; if (accessToken) { headers.set('Authorization', `Bearer ${accessToken}`); } return headers; }, }); // 带自动重鉴权的请求逻辑 const baseQueryWithReauth = async (args, api, extraOptions) => { let result = await baseQuery(args, api, extraOptions); // 捕获403错误,触发令牌刷新 if (result.error && result.error.status === 403) { if (!refreshTokenPromise) { // 发起刷新请求,并用Promise锁定,防止重复调用 refreshTokenPromise = api.dispatch(authApi.endpoints.refreshTokens.initiate(null)) .unwrap() .then((newAuthData) => { // 更新Redux Store中的访问令牌 api.dispatch({ type: 'auth/setAccessToken', payload: newAuthData.accessToken, }); return newAuthData; }) .finally(() => { // 请求完成后释放锁 refreshTokenPromise = null; }); } try { // 等待刷新完成,重试原请求 await refreshTokenPromise; result = await baseQuery(args, api, extraOptions); } catch (refreshError) { // 刷新失败(如刷新令牌过期),执行登出逻辑 api.dispatch({ type: 'auth/logout' }); } } return result; };
步骤3:在主API中使用自定义BaseQuery
将所有业务API的baseQuery替换为上面的baseQueryWithReauth,这样所有请求都会自动触发全局刷新逻辑:
import { createApi } from '@reduxjs/toolkit/query/react'; import { baseQueryWithReauth } from './baseQueryWithReauth'; export const mainApi = createApi({ reducerPath: 'mainApi', baseQuery: baseQueryWithReauth, // 替换为带重鉴权的baseQuery endpoints: (builder) => ({ updateProfile: builder.mutation({ query: (body) => ({ url: '/profile', method: 'PUT', body, }), }), // 其他业务接口... }), }); export const { useUpdateProfileMutation } = mainApi;
关键细节说明
- 无侵入式逻辑:业务代码无需再手动处理403和重试,所有拦截、刷新、重试逻辑都在全局baseQuery中完成。
- 并发请求防护:通过
refreshTokenPromise全局锁,确保多个请求同时触发403时,只会调用一次/refresh端点,避免资源浪费和冲突。 - 失败处理:如果刷新令牌也过期,直接触发登出逻辑,引导用户重新登录。
- 令牌更新:刷新成功后必须更新Redux Store中的访问令牌,确保重试请求和后续请求能使用新令牌。
内容的提问来源于stack exchange,提问作者damdafayton
相关产品推荐
相关产品推荐

