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

React Native+Redux实现OAuth令牌自动刷新的技术方案咨询

嘿,这个场景我之前做React Native项目的时候刚好碰到过,折腾了一阵终于找到比较流畅的实现方式,分享给你参考下~

实现思路与步骤

核心就是把令牌的有效性校验、自动刷新、状态更新这一套逻辑封装成可复用的工具,让所有API请求都自动走这个流程,不用每个action creator都重复写token处理代码,完全贴合Redux的单向数据流规则。

1. 先在Redux里定义令牌相关状态

首先得在Store里存好必要的令牌信息,用Redux Toolkit写个slice会很方便:

// src/features/auth/authSlice.js
import { createSlice } from '@reduxjs/toolkit';

const initialState = {
  accessToken: null,
  refreshToken: null,
  tokenExpiry: null, // 存储accessToken的过期时间戳(比如 new Date().getTime() + expiresIn*1000)
  isRefreshingToken: false, // 标记是否正在刷新token,避免并发请求重复触发刷新
};

const authSlice = createSlice({
  name: 'auth',
  initialState,
  reducers: {
    setTokens: (state, action) => {
      const { accessToken, refreshToken, expiresIn } = action.payload;
      state.accessToken = accessToken;
      state.refreshToken = refreshToken;
      state.tokenExpiry = new Date().getTime() + expiresIn * 1000;
    },
    setRefreshingStatus: (state, action) => {
      state.isRefreshingToken = action.payload;
    },
    clearTokens: (state) => {
      Object.assign(state, initialState);
    },
  },
});

export const { setTokens, setRefreshingStatus, clearTokens } = authSlice.actions;
export default authSlice.reducer;

2. 封装令牌刷新的核心Thunk函数

写一个异步Thunk,专门负责获取有效accessToken,内部自动处理过期判断和刷新逻辑:

// src/features/auth/authThunks.js
import { createAsyncThunk } from '@reduxjs/toolkit';
import { setTokens, setRefreshingStatus, clearTokens } from './authSlice';
import apiClient from '../../utils/apiClient';

// 提前30秒就视为即将过期,提前触发刷新
const EXPIRY_BUFFER = 30 * 1000;

export const getValidAccessToken = createAsyncThunk(
  'auth/getValidAccessToken',
  async (_, { getState, dispatch }) => {
    const { auth } = getState();
    const { accessToken, tokenExpiry, refreshToken, isRefreshingToken } = auth;

    // 如果正在刷新token,稍等一下再返回最新token
    if (isRefreshingToken) {
      await new Promise(resolve => setTimeout(resolve, 500));
      return getState().auth.accessToken;
    }

    // 检查token是否有效且未过期
    if (accessToken && tokenExpiry && new Date().getTime() + EXPIRY_BUFFER < tokenExpiry) {
      return accessToken;
    }

    // 没有refreshToken直接清空状态跳登录
    if (!refreshToken) {
      dispatch(clearTokens());
      throw new Error('请重新登录');
    }

    try {
      dispatch(setRefreshingStatus(true));
      // 调用后端刷新token接口
      const res = await apiClient.post('/auth/refresh', { refreshToken });
      const { accessToken: newAT, refreshToken: newRT, expiresIn } = res.data;
      
      // 更新Store里的令牌信息
      dispatch(setTokens({ accessToken: newAT, refreshToken: newRT, expiresIn }));
      return newAT;
    } catch (err) {
      // 刷新失败,清空令牌跳登录
      dispatch(clearTokens());
      throw err;
    } finally {
      dispatch(setRefreshingStatus(false));
    }
  }
);

3. 封装全局API请求拦截器

把所有API请求都通过这个封装好的客户端发送,自动帮你处理token的添加和刷新:

// src/utils/apiClient.js
import axios from 'axios';
import { store } from '../store'; // 你的Redux Store实例
import { getValidAccessToken } from '../features/auth/authThunks';

const apiClient = axios.create({
  baseURL: 'https://your-api-domain.com',
});

// 请求拦截器:自动添加有效token到请求头
apiClient.interceptors.request.use(
  async (config) => {
    const accessToken = await store.dispatch(getValidAccessToken()).unwrap();
    if (accessToken) {
      config.headers.Authorization = `Bearer ${accessToken}`;
    }
    return config;
  },
  (err) => Promise.reject(err)
);

// 响应拦截器:处理突发401的情况(比如token突然失效)
apiClient.interceptors.response.use(
  (res) => res,
  async (err) => {
    if (err.response?.status === 401) {
      try {
        // 尝试重新刷新token并重发请求
        const newAT = await store.dispatch(getValidAccessToken()).unwrap();
        err.config.headers.Authorization = `Bearer ${newAT}`;
        return apiClient(err.config);
      } catch (refreshErr) {
        // 刷新失败,跳登录页(这里用你项目的导航方式,比如React Navigation)
        // navigationRef.navigate('Login');
        return Promise.reject(refreshErr);
      }
    }
    return Promise.reject(err);
  }
);

export default apiClient;

4. 在业务Action Creator里直接用封装好的API

现在你的业务代码完全不用关心token的问题,直接调用apiClient就行:

// src/features/user/userThunks.js
import { createAsyncThunk } from '@reduxjs/toolkit';
import apiClient from '../../utils/apiClient';

export const fetchUserProfile = createAsyncThunk(
  'user/fetchProfile',
  async () => {
    const res = await apiClient.get('/user/profile');
    return res.data;
  }
);

一些关键注意事项

  • 并发请求防重复刷新:通过isRefreshingToken状态避免多个请求同时触发刷新接口,这个非常重要,不然会导致后端重复生成token。
  • 过期时间计算要准确:一定要把后端返回的expiresIn(秒数)转换成时间戳存在Store里,不要靠猜时间。
  • 刷新失败的降级处理:如果refreshToken也失效了,必须清空状态跳登录,避免无限循环刷新。

这样整个流程就完全闭环了,每次发起API请求前都会自动确保token有效,完全符合Redux的单向数据流原则~

内容的提问来源于stack exchange,提问作者BlackLog

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:06:45