You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 07:54:18