同域React应用基于localStorage校验加载时重定向至登录应用的最佳实践
刚好做过类似的同域名多React应用的身份校验方案,给你分享几个经过项目验证的最佳实践,既能满足需求又兼顾安全性和可维护性:
核心思路
同域名下的前端应用共享localStorage,这是实现跨应用身份状态同步的基础。但直接读写localStorage要注意安全风险(比如XSS攻击),同时要确保登录状态校验的逻辑统一,避免重复代码。
具体实现方案
1. 封装通用的身份校验工具类(跨应用复用)
最好把身份相关的逻辑抽成独立的工具文件(甚至可以做成私有npm包在两个应用间共享),避免在两个应用里重复写相同的校验、存token逻辑。
比如创建一个auth.utils.js:
// auth.utils.js const AUTH_TOKEN_KEY = 'app_auth_token'; const LOGIN_PAGE_PATH = '/login'; // 登录应用的路由,同域名直接用相对路径 const REDIRECT_KEY = 'redirect_after_login'; // 获取当前登录token export const getAuthToken = () => localStorage.getItem(AUTH_TOKEN_KEY); // 存储登录token export const setAuthToken = (token) => localStorage.setItem(AUTH_TOKEN_KEY, token); // 清除登录token export const clearAuthToken = () => localStorage.removeItem(AUTH_TOKEN_KEY); // 检查登录状态,未登录则重定向到登录页 export const checkAuth = () => { const token = getAuthToken(); // 无token直接重定向,同时记录当前页面路径,登录成功后跳转回来 if (!token) { const currentPath = `${window.location.pathname}${window.location.search}`; localStorage.setItem(REDIRECT_KEY, currentPath); window.location.href = LOGIN_PAGE_PATH; return false; } // 可选:校验token有效性(比如JWT的过期时间) try { const decodedPayload = JSON.parse(atob(token.split('.')[1])); if (decodedPayload.exp * 1000 < Date.now()) { clearAuthToken(); window.location.href = LOGIN_PAGE_PATH; return false; } } catch (err) { // token格式错误,直接清除并重定向 clearAuthToken(); window.location.href = LOGIN_PAGE_PATH; return false; } return true; };
在登录应用的登录成功逻辑里,调用setAuthToken并处理跳转:
// 登录应用的登录提交函数 const handleLoginSubmit = async (formData) => { const loginRes = await api.post('/api/login', formData); if (loginRes.success) { setAuthToken(loginRes.token); // 跳转到之前保存的页面,默认跳转到业务应用首页 const redirectPath = localStorage.getItem(REDIRECT_KEY) || '/dashboard'; localStorage.removeItem(REDIRECT_KEY); window.location.href = redirectPath; } };
2. 业务应用设置全局路由守卫
在业务应用的路由入口(比如根组件或者路由配置文件),全局校验登录状态,确保未登录用户无法访问需要权限的页面。
如果用react-router-dom,可以封装一个高阶组件作为路由守卫:
// 业务应用的路由守卫组件 import { checkAuth } from './auth.utils'; export const ProtectedRoute = ({ children }) => { const isAuthenticated = checkAuth(); // 未登录的话checkAuth已经会重定向,这里只需要返回授权内容或者null return isAuthenticated ? children : null; };
然后在路由配置里使用:
// 业务应用的App.js import { Routes, Route } from 'react-router-dom'; import { ProtectedRoute } from './ProtectedRoute'; import PublicPage from './pages/PublicPage'; import Dashboard from './pages/Dashboard'; import Settings from './pages/Settings'; function App() { return ( <Routes> {/* 无需登录的公开页面 */} <Route path="/public" element={<PublicPage />} /> {/* 需要登录的页面用ProtectedRoute包裹 */} <Route path="/dashboard" element={ <ProtectedRoute> <Dashboard /> </ProtectedRoute> } /> <Route path="/settings" element={ <ProtectedRoute> <Settings /> </ProtectedRoute> } /> </Routes> ); }
也可以在根组件里用useEffect做全局校验,覆盖所有页面:
// 业务应用的根组件 import { useEffect } from 'react'; import { checkAuth } from './auth.utils'; function App() { useEffect(() => { // 组件加载时立即校验登录状态 checkAuth(); }, []); // ... 路由内容 }
3. 安全性增强措施
因为localStorage容易被XSS攻击窃取,所以必须做这些防护:
- 过滤所有用户输入:确保应用内的用户输入都经过转义处理,避免注入恶意脚本
- 给token设置短过期时间:即使token被窃取,有效期也有限,同时定期在业务应用里校验token的过期时间
- 监听storage事件,实时同步状态:如果用户在另一个标签页登录/登出,业务应用可以实时感知并处理:
// 业务应用的根组件里添加监听 useEffect(() => { const handleStorageChange = (e) => { if (e.key === 'app_auth_token') { // token被清除,说明用户登出了,立即重定向到登录页 if (!e.newValue) { window.location.href = '/login'; } } }; window.addEventListener('storage', handleStorageChange); return () => window.removeEventListener('storage', handleStorageChange); }, []);
4. 统一登出逻辑
两个应用都使用同一个clearAuthToken方法,确保登出状态同步:
// 业务应用的登出按钮逻辑 import { clearAuthToken } from './auth.utils'; const handleLogout = () => { clearAuthToken(); window.location.href = '/login'; };
总结
核心就是利用同域名localStorage共享的特性,通过统一的身份工具类确保两个应用的校验逻辑一致,在业务应用的路由层做全局守卫拦截未登录请求,同时通过XSS防护和token校验提升安全性。
内容的提问来源于stack exchange,提问作者Lakshmi Prasanna
相关产品推荐
相关产品推荐

