如何配置React应用实现点击链接后触发Keycloak登录
问题描述
我用create-react-app创建了一个React应用,配置了简单路由,期望实现以下流程:用户进入首页后点击按钮,跳转到Keycloak登录界面,登录完成后进入UserPage。我已在UserPage中按照官方文档配置了keycloak-js,但未达到预期效果,请问该如何实现这一流程?
现有代码
App.js
import {BrowserRouter, Routes, Route} from 'react-router-dom'; import './App.css'; import Homepage from './pages/Homepage'; import UserPage from './pages/UserPage'; import ErrorPage from './pages/ErrorPage'; function App() { return ( <div className="App"> <BrowserRouter> <Routes> <Route index element={<Homepage/>}/> <Route path="/home" element={<Homepage/>}/> <Route path="/login" element={<UserPage/>}/> <Route path="*" element={<ErrorPage/>}/> </Routes> </BrowserRouter> </div> ); } export default App;
UserPage.js(Keycloak配置位置)
import Header from "../components/Header"; import React from 'react'; import Keycloak from 'keycloak-js' const kcConfig = { "url": "http://localhost:8080", "realm": "react-realm", "clientId": "react-client" } const kc = new Keycloak(kcConfig); try { const authenticated = await kc.init({onLoad: 'login-required', pkceMethod: 'S256', checkLoginIframe: false}); console.log(`User is ${authenticated ? 'authenticated' : 'not authenticated'}`); } catch (error) { console.error('Failed to initialize adapter:', error); } function UserPage() { return ( <div> <Header/> <h2>Página do usuário</h2> <p>Aqui estarão informações do usuário</p> <button><a href="/">Voltar</a></button> </div> ) } export default UserPage
解决方案
1. 修复Keycloak初始化错误
你的代码存在两个核心问题:
await不能直接在组件外部使用,必须包裹在async函数内- Keycloak初始化逻辑放在组件外部,会在组件加载前执行,不符合React生命周期规范
修改后的UserPage.js:
import Header from "../components/Header"; import React, { useEffect, useState } from 'react'; import Keycloak from 'keycloak-js' const kcConfig = { "url": "http://localhost:8080", "realm": "react-realm", "clientId": "react-client" } const kc = new Keycloak(kcConfig); function UserPage() { const [isAuthenticated, setIsAuthenticated] = useState(false); const [loading, setLoading] = useState(true); useEffect(() => { const initKeycloak = async () => { try { const authenticated = await kc.init({ onLoad: 'login-required', pkceMethod: 'S256', checkLoginIframe: false, redirectUri: window.location.origin + '/login' // 需与Keycloak客户端配置的回调地址一致 }); setIsAuthenticated(authenticated); console.log(`User is ${authenticated ? 'authenticated' : 'not authenticated'}`); } catch (error) { console.error('Failed to initialize adapter:', error); } finally { setLoading(false); } }; initKeycloak(); }, []); if (loading) { return <div>加载中...</div>; } return ( <div> <Header/> <h2>用户页面</h2> <p>这里将显示用户信息</p> <button><a href="/">返回首页</a></button> <button onClick={() => kc.logout()}>登出</button> </div> ) } export default UserPage
2. 调整路由与首页逻辑
- 首页按钮直接跳转至
/login路由,onLoad: 'login-required'会自动触发Keycloak登录流程 - 确保Keycloak客户端配置的Valid Redirect URIs包含
http://localhost:3000/login(你的React应用地址),否则登录后会被拒绝回调
首页组件示例(Homepage.jsx):
import { Link } from 'react-router-dom'; function Homepage() { return ( <div> <h1>首页</h1> <Link to="/login"> <button>登录进入用户页面</button> </Link> </div> ); } export default Homepage;
3. 可选:添加路由保护
如果希望未登录用户无法直接访问UserPage,可创建路由保护组件:
// components/ProtectedRoute.jsx import React, { useEffect, useState } from 'react'; import { Navigate } from 'react-router-dom'; import Keycloak from 'keycloak-js'; const kcConfig = { "url": "http://localhost:8080", "realm": "react-realm", "clientId": "react-client" }; const kc = new Keycloak(kcConfig); const ProtectedRoute = ({ children }) => { const [isAuthenticated, setIsAuthenticated] = useState(null); useEffect(() => { const checkAuth = async () => { try { const authenticated = await kc.init({ onLoad: 'check-sso', pkceMethod: 'S256', checkLoginIframe: false }); setIsAuthenticated(authenticated); } catch (error) { console.error('Auth check failed:', error); setIsAuthenticated(false); } }; checkAuth(); }, []); if (isAuthenticated === null) { return <div>验证中...</div>; } return isAuthenticated ? children : <Navigate to="/" />; }; export default ProtectedRoute;
修改App.js,用保护组件包裹UserPage:
import {BrowserRouter, Routes, Route} from 'react-router-dom'; import './App.css'; import Homepage from './pages/Homepage'; import UserPage from './pages/UserPage'; import ErrorPage from './pages/ErrorPage'; import ProtectedRoute from './components/ProtectedRoute'; function App() { return ( <div className="App"> <BrowserRouter> <Routes> <Route index element={<Homepage/>}/> <Route path="/home" element={<Homepage/>}/> <Route path="/login" element={ <ProtectedRoute> <UserPage/> </ProtectedRoute> }/> <Route path="*" element={<ErrorPage/>}/> </Routes> </BrowserRouter> </div> ); } export default App;
内容的提问来源于stack exchange,提问作者Felipe Mastromauro Corrêa
相关产品推荐
相关产品推荐

