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

React+Keycloak认证中Redux Saga在Token设置前调用API的问题

解决React+Keycloak刷新页面时Saga提前发起API请求的问题

当前React应用基于Keycloak实现身份认证,正常流程运行正常,但刷新页面后,Redux Saga会在Keycloak Token完成设置前发起API调用,导致请求未携带有效授权Token。以下是几个可行的解决方案:


方案一:在Saga中等待Token就绪

让发起API请求的Saga先等待Redux中auth.token存在后再执行请求,避免提前调用。

修改后的Saga代码

function* waitForValidToken() {
  // 轮询等待token非空
  while (true) {
    const { auth } = yield select();
    if (auth.token) return;
    yield delay(100); // 短间隔轮询,避免阻塞
  }
}

function* getAPI(action) {
  // 先等待Token就绪
  yield call(waitForValidToken);
  
  const { auth } = yield select();
  try {
    // 提前设置授权头,无需作为参数传入axiosRequest
    setAuthorizationBearer(auth.token);
    const response = yield call(
      axiosRequest,
      "get",
      BaseURL,
      `/foo/mini`,
      {},
      null,
      { sendToken: true },
      "application/json"
    );
    yield put({ type: `${action.type}_SUCCESS`, payload: response, metadata: action.metadata })
  } catch (e) {
    yield put({ type: `${action.type}_ERROR`, payload: e })
  }
}

方案二:基于Keycloak初始化状态控制组件渲染

通过ReactKeycloakProvider的初始化状态,控制触发API请求的组件或Redux Action的执行时机,确保只有Token就绪后才发起请求。

步骤1:添加初始化状态到Redux

// auth reducer
const initialState = {
  token: null,
  isAuthInitialized: false
};

export default function authReducer(state = initialState, action) {
  switch(action.type) {
    case 'AUTH_USER':
      return { ...state, token: action.payload };
    case 'AUTH_INITIALIZED':
      return { ...state, isAuthInitialized: true };
    default:
      return state;
  }
}

步骤2:修改ReactKeycloakProvider配置

const dispatch = useDispatch()
const { isAuthInitialized } = useSelector(state => state.auth)

<ReactKeycloakProvider 
  onTokens={({token}) => dispatch({ type: 'AUTH_USER', payload: token })} 
  onReady={() => dispatch({ type: 'AUTH_INITIALIZED' })}
  authClient={Keycloak(config)}
  initOptions={{
    onLoad: 'login-required',
    checkLoginIframe: false,
    timeSkew: "0",
    refreshToken: ""
  }}
  LoadingComponent={<div />}
>
  {/* 仅当认证初始化完成后,才渲染触发API请求的内容 */}
  {isAuthInitialized && <AppMainContent />}
</ReactKeycloakProvider>

方案三:直接从Keycloak实例获取Token(推荐)

放弃依赖Redux同步Token的方式,每次API请求时直接从Keycloak客户端实例获取最新Token,从根源上避免时序问题。

步骤1:导出Keycloak实例

// keycloak.js
import Keycloak from 'keycloak-js';
const keycloakConfig = { /* 你的Keycloak配置 */ };
export const keycloak = new Keycloak(keycloakConfig);

步骤2:修改Axios请求实例

import axios from "axios";
import { keycloak } from './keycloak';

const instance = (
  method,
  baseURL = process.env.REACT_APP_ENDPOINT,
  url,
  data = null,
  headers = null,
  sendToken = true,
  contentType
) => {
  return new Promise((resolve, reject) => {
    const reqHeaders = { ...headers };
    
    // 直接从Keycloak实例取Token
    if (sendToken && keycloak.token) {
      reqHeaders.Authorization = `Bearer ${keycloak.token}`;
      reqHeaders["Content-Type"] = contentType || "application/json";
    }

    axios({
      method,
      baseURL,
      url,
      data,
      headers: reqHeaders,
      timeout: 30000,
      crossDomain: true,
    })
      .then((payload) => {
        payload.status < 400 ? resolve(payload) : reject(payload);
      })
      .catch((e) => {
        if (axios.isCancel(e)) console.log("Request canceled", e.message);
        reject(e);
      });
  });
};

export default instance;

原代码的小问题修正

原Saga代码中,将setAuthorizationBearer(state.auth.token)作为参数传给axiosRequest是错误的——该函数是同步设置全局变量,无需作为参数传递,应提前调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 20:36:22