如何使用React Router Dom V6实现不同用户角色的路由访问限制
如何使用React Router Dom V6实现不同用户角色的路由访问限制
我来帮你搞定这个路由权限的问题~你现在的核心问题是:虽然登录后能跳转到对应角色的页面,但手动修改地址栏就能跨角色访问,这是因为你当前的路由写法只是把不同角色的路由分开成独立的Routes组件,并没有做任何权限校验,React Router会直接匹配到对应的路由并渲染。
咱们可以通过自定义受保护路由组件的方式来解决这个问题,具体步骤如下:
第一步:创建访问拒绝页面
先做一个简单的AccessDenied页面,用来提示用户没有权限:
// AccessDenied.jsx const AccessDenied = () => { return ( <div style={{ textAlign: 'center', marginTop: '50px' }}> <h1>访问被拒绝</h1> <p>您没有权限访问该页面,请确认您的用户角色</p> </div> ); }; export default AccessDenied;
第二步:完善登录逻辑,存储用户角色
首先要确保登录成功后,把用户的角色信息存在本地(比如localStorage,如果用了Redux/Context也可以存在状态里),这样后续路由校验才能拿到角色:
// 登录页面的登录逻辑修改 if (res.data.data.user?.role?.name.toLowerCase() === "vendor") { // 把用户信息存在localStorage localStorage.setItem('user', JSON.stringify(res.data.data.user)); navigate("/dashboard"); } else { localStorage.setItem('user', JSON.stringify(res.data.data.user)); navigate("/buyer-dashboard"); }
第三步:编写角色校验的自定义路由组件
创建一个RequireRole组件,用来检查当前用户的角色是否符合路由的访问要求,不符合就跳转到访问拒绝页面:
// RequireRole.jsx import { Navigate, Outlet } from 'react-router-dom'; const RequireRole = ({ allowedRoles }) => { // 从localStorage获取用户信息(如果用状态管理就从状态里取) const user = JSON.parse(localStorage.getItem('user')); const userRole = user?.role?.name.toLowerCase(); // 如果用户未登录,直接跳转到登录页 if (!user) { return <Navigate to="/" replace />; } // 如果用户角色不在允许的列表里,跳转到访问拒绝页 if (!allowedRoles.includes(userRole)) { return <Navigate to="/access-denied" replace />; } // 权限校验通过,渲染嵌套的子路由 return <Outlet />; }; export default RequireRole;
第四步:重构App.jsx的路由结构
把所有路由整合到一个Routes组件里,用RequireRole来包裹对应角色的路由,实现权限控制:
// App.jsx import { BrowserRouter, Routes, Route } from 'react-router-dom'; import RequireRole from './RequireRole'; import AccessDenied from './AccessDenied'; // 导入你的其他组件 import Login from './Login'; import SignUp from './SignUp'; import Confirmation from './Confirmation'; import ForgetPassword from './ForgetPassword'; import SetPassword from './SetPassword'; import Dashboard from './Dashboard'; import Other from './Other'; import VendorDashboard from './VendorDashboard'; import Items from './Items'; function App() { return ( <BrowserRouter> <Routes> {/* 公共路由:所有用户(包括未登录)都能访问 */} <Route index element={<Login />} /> <Route path='/signup' element={<SignUp />} /> <Route path='/email-confirmation' element={<Confirmation />} /> <Route path='/forget-password' element={<ForgetPassword />} /> <Route path='/set-password/:email' element={<SetPassword />} /> <Route path='/access-denied' element={<AccessDenied />} /> {/* Vendor专属路由:只有vendor角色能访问 */} <Route element={<RequireRole allowedRoles={['vendor']} />}> <Route path="/dashboard" element={<VendorDashboard />} /> <Route path="/items" element={<Items />} /> </Route> {/* Buyer专属路由:只有buyer角色能访问 */} <Route element={<RequireRole allowedRoles={['buyer']} />}> <Route path="/buyer-dashboard" element={<Dashboard />} /> <Route path="/other" element={<Other />} /> </Route> {/* 404页面:匹配所有未定义的路由 */} <Route path='*' element={<div style={{ textAlign: 'center', marginTop: '50px' }}>404 页面不存在</div>} /> </Routes> </BrowserRouter> ); } export default App;
为什么这样改能解决问题?
你之前的写法是把三个独立的Routes组件放在BrowserRouter里,React Router V6会依次匹配这些Routes里的路由,完全不做权限校验。现在的写法是把所有路由统一管理,用Outlet嵌套受保护的子路由,只有通过RequireRole的角色校验,才会渲染对应的子页面,否则直接跳转到访问拒绝或登录页。
另外提个小建议:如果你的应用比较复杂,建议用Context或者Redux来管理用户状态,比localStorage更安全(避免XSS攻击),退出登录的时候记得清除用户信息哦~
备注:内容来源于stack exchange,提问作者InteleJay
相关产品推荐
相关产品推荐

