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

如何基于Keycloak实现React SPA的可选身份认证?

实现Keycloak可选身份认证的方案

核心修改思路

将原来强制登录的初始化配置改为仅检查登录状态,允许未认证用户直接访问站点,同时在需要认证的场景(如访问受保护内容、执行敏感操作)提供登录入口。


步骤1:修改Keycloak初始化配置

将onLoad参数从"login-required"替换为"check-sso",该模式会静默检查用户是否已登录(通过Cookie或会话),但不会强制跳转登录页;无论认证结果如何,都会完成初始化并渲染应用。

修改后的index.js代码:

window.keycloak
  .init({
    onLoad: "check-sso", // 替换为check-sso,取消强制登录
    checkLoginIframe: false,
    promiseType: "native",
  })
  .then((authenticated) => {
    // 无论用户是否认证,都渲染应用
    ReactDOM.render(
      <App keycloak={window.keycloak} isAuthenticated={authenticated} />,
      document.getElementById("root")
    );
  })
  .catch(() => {
    console.error("Keycloak初始化失败");
  });

步骤2:在应用内实现登录/登出逻辑

在App组件中添加登录、登出按钮,并通过Keycloak实例的login()/logout()方法触发跳转:

function App({ keycloak, isAuthenticated }) {
  const handleLogin = () => {
    // 登录后跳转回当前页面,可自定义redirectUri
    keycloak.login({ redirectUri: window.location.href });
  };

  const handleLogout = () => {
    keycloak.logout({ redirectUri: window.location.href });
  };

  return (
    <div>
      <nav>
        {isAuthenticated ? (
          <>
            <span>当前用户:{keycloak.tokenParsed.preferred_username}</span>
            <button onClick={handleLogout}>登出</button>
          </>
        ) : (
          <button onClick={handleLogin}>登录</button>
        )}
      </nav>
      
      {/* 应用核心内容 */}
      <Routes>
        {/* 公开页面:所有用户可访问 */}
        <Route path="/" element={<HomePage />} />
        {/* 受保护页面:仅认证用户可访问 */}
        <Route 
          path="/dashboard" 
          element={isAuthenticated ? <Dashboard /> : <NeedLoginPrompt />} 
        />
      </Routes>
    </div>
  );
}

步骤3:全局管理认证状态(可选优化)

通过React Context封装Keycloak状态,避免逐层传递props,方便所有组件快速获取认证状态和操作方法:

1. 创建Keycloak Context

import { createContext, useContext, useEffect, useState } from 'react';

const KeycloakContext = createContext(null);

export function KeycloakProvider({ children, keycloak }) {
  const [isAuthenticated, setIsAuthenticated] = useState(keycloak.authenticated);
  const [userInfo, setUserInfo] = useState(keycloak.tokenParsed);

  useEffect(() => {
    // 监听登录/登出状态变化
    const handleAuthSuccess = () => {
      setIsAuthenticated(true);
      setUserInfo(keycloak.tokenParsed);
    };
    const handleAuthLogout = () => {
      setIsAuthenticated(false);
      setUserInfo(null);
    };

    keycloak.onAuthSuccess = handleAuthSuccess;
    keycloak.onAuthLogout = handleAuthLogout;

    // 清理监听
    return () => {
      delete keycloak.onAuthSuccess;
      delete keycloak.onAuthLogout;
    };
  }, [keycloak]);

  return (
    <KeycloakContext.Provider value={{ keycloak, isAuthenticated, userInfo }}>
      {children}
    </KeycloakContext.Provider>
  );
}

export function useKeycloak() {
  const context = useContext(KeycloakContext);
  if (!context) {
    throw new Error('useKeycloak必须在KeycloakProvider内部使用');
  }
  return context;
}

2. 在index.js中使用Provider

window.keycloak.init({
  onLoad: "check-sso",
  checkLoginIframe: false,
  promiseType: "native",
}).then(() => {
  ReactDOM.render(
    <KeycloakProvider keycloak={window.keycloak}>
      <App />
    </KeycloakProvider>,
    document.getElementById("root")
  );
});

3. 在任意组件中使用状态

import { useKeycloak } from './KeycloakContext';

function ProtectedContent() {
  const { isAuthenticated, keycloak } = useKeycloak();

  if (!isAuthenticated) {
    return (
      <div>
        <p>此内容需要登录后访问</p>
        <button onClick={() => keycloak.login()}>立即登录</button>
      </div>
    );
  }

  return <div>欢迎访问受保护内容</div>;
}

关键细节说明

  • check-sso模式:初始化时会静默检测用户是否已有有效登录会话(如之前登录过未登出),如果有则自动获取Token,无需用户再次登录。
  • Token自动刷新:Keycloak默认会自动刷新过期的Access Token,无需手动处理;若需监听刷新事件,可添加onAuthRefreshSuccess/onAuthRefreshError回调。
  • 登录跳转控制:调用login()时可通过redirectUri指定登录完成后跳转的页面,默认会回到应用首页。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 22:15:15