React-router-dom v6结合Keycloak OAuth2实现认证后跳转至目标页面
解决React集成Keycloak的两个核心问题
一、页面重载时保留认证数据
当前AuthProvider的状态只存在内存中,页面刷新就会丢失,必须把认证数据持久化到本地存储:
同步更新本地存储
从后端拿到access_token和用户信息后,除了更新组件state,还要存到localStorage(如果希望关闭浏览器就失效,改用sessionStorage):// AuthProvider中的状态更新方法 const setAuth = (token, userInfo) => { setToken(token); setUser(userInfo); // 持久化到本地 localStorage.setItem('authToken', token); localStorage.setItem('authUser', JSON.stringify(userInfo)); };初始化时恢复状态
在AuthProvider挂载时,先读取本地存储的已有数据,恢复认证状态:useEffect(() => { const savedToken = localStorage.getItem('authToken'); const savedUser = localStorage.getItem('authUser'); if (savedToken && savedUser) { setToken(savedToken); setUser(JSON.parse(savedUser)); } }, []);登出时清除残留数据
登出操作要同步删除本地存储的认证信息,避免遗留:const logout = () => { setToken(null); setUser(null); localStorage.removeItem('authToken'); localStorage.removeItem('authUser'); // 跳Keycloak登出页或首页 window.location.href = `${keycloakUrl}/logout?redirect_uri=${window.location.origin}`; };
二、认证完成后跳转回原访问页面
需要在跳转到Keycloak前记录用户最初访问的路径,认证完成后再跳转回去:
路由守卫中记录原路径
用React Router自定义PrivateRoute组件,检测到未认证时,先保存当前路径再跳Keycloak授权页:const PrivateRoute = ({ children }) => { const { token } = useAuth(); const location = useLocation(); if (!token) { // 排除/auth页面,避免循环跳转 if (location.pathname !== '/auth') { // 保存完整路径(包含查询参数) localStorage.setItem('redirectPath', `${location.pathname}${location.search}`); } // 替换成你的Keycloak授权地址 const keycloakAuthUrl = `${keycloakUrl}/auth?client_id=你的客户端ID&redirect_uri=${encodeURIComponent(window.location.origin + '/auth')}`; return <Navigate to={keycloakAuthUrl} replace />; } return children; };/auth页面完成认证后跳转
在/auth页面拿到code、换取token并更新AuthProvider后,读取保存的路径跳转:const AuthPage = () => { const { setAuth } = useAuth(); const navigate = useNavigate(); useEffect(() => { const code = new URLSearchParams(window.location.search).get('code'); if (code) { fetch(`/api/token?code=${code}`) .then(res => res.json()) .then(data => { setAuth(data.access_token, data.userInfo); // 读取原路径,默认跳首页 const redirectPath = localStorage.getItem('redirectPath') || '/'; // 跳转后清除存储的路径 localStorage.removeItem('redirectPath'); navigate(redirectPath, { replace: true }); }) .catch(() => navigate('/')); } }, [setAuth, navigate]); return <div>正在认证...</div>; };
额外注意事项
- 统一使用
localStorage或sessionStorage,不要混存避免逻辑混乱。 - Keycloak控制台配置的
redirect_uri必须和代码中的一致,否则会授权失败。 - 对安全性要求高的场景,可以把原路径加密后放到Keycloak授权请求的
state参数中,比本地存储更安全,避免被篡改。
内容的提问来源于stack exchange,提问作者Krishna
相关产品推荐
相关产品推荐

