React实现RBAC后登录跳转页面空白问题排查
React RBAC权限控制页面空白问题排查
我正在React应用中实现RBAC(基于角色的访问控制),后端采用Node.js且运行正常,前端使用Vite + react-router-dom v6。目前将从服务器获取的token和role存储在Session Storage中,应用包含basic和admin两个角色,登录成功后会根据返回角色跳转,用HOC作为路由中间件校验权限。现在跳转功能正常,但两个角色对应的页面均显示空白(本该渲染简单文本),求排查问题。
登录页面(Login.js)
import { useState } from 'react' import { useNavigate } from 'react-router-dom'; import axios from 'axios'; const Login = () => { const navigate = useNavigate() const [input, setInput] = useState({ email:"", password:"" }) const handleInputChange = (e) => { const {name, value} = e.target; setInput((prev) => ({ ...prev, [name]:value })) }; const handleSubmit = async (e) => { e.preventDefault(); console.log(input); const url = "http://localhost:8080/auth/login"; try { const response = await axios.post(`${url}`, input, {headers:{ 'content-type':'application/json' }} ); console.log(response); if(response.status === 200) { console.log("ok"); const token = response.data.token; const role = response.data.role console.log({"token":token, "role":role}) sessionStorage.setItem('token', token); sessionStorage.setItem('role', role); if(role === "admin"){ navigate('/admin'); } else if( role === "basic") { navigate('/basic'); } else { navigate('/login'); } } } catch(error) { console.log({"error":error.response}) } } return ( <div> <h1>Log In</h1> <form onSubmit={handleSubmit}> <p>email:</p> <input type="text" name="email" value={input.email} onChange={handleInputChange}/> <p>password:</p> <input type="text" name="password" value={input.password} onChange={handleInputChange}/><br/> <button type='submit'>Login</button> </form> </div> ) } export default Login
路由布局(App.js)
import { } from 'react' import './App.css' import { Route, Routes } from 'react-router' import Access from './Auth/Access' import Home from './pages/Home' import Login from './pages/Login' import Signup from './pages/Signup' import Admin from './pages/Admin' import Basic from './pages/Basic' import { Error } from './pages/Error' function App() { return ( <div> <Routes> <Route path='/' element={<Home/>}/> <Route path='/login' element={<Login/>}/> <Route path='/signup' element={<Signup/>}/> <Route path='/basic' element={<Access role={'basic'}> <Basic/></Access>}/> <Route path='/admin' element={<Access role={'admin'}><Admin/></Access>}/> <Route path='*' element={<Error/>}/> </Routes> </div> ) } export default App
权限校验HOC(Access.js)
import { Navigate,Outlet } from "react-router-dom"; const Access = ({ role }) => { const userRole = sessionStorage.getItem("role"); const token = sessionStorage.getItem('token'); if (!token || (role && role !== userRole)) { // 无token或角色不匹配时跳转到登录页 return <Navigate to="/login" replace />; } // 权限通过,渲染嵌套组件 return <Outlet />; }; export default Access
Admin页面
/* eslint-disable no-unused-vars */ import React from "react" const Admin = () => { return ( <div>wecome to admin page</div> ) } export default Admin
Basic页面
// eslint-disable-next-line no-unused-vars import React from 'react' const Basic = () => { return ( <div>welcome to basic page</div> ) } export default Basic
问题原因及修复
页面空白的核心原因是Access组件中Outlet的用法错误:
react-router-dom v6中,Outlet仅用于渲染嵌套路由结构的子路由内容,但你当前是直接将Basic/Admin作为Access的子元素传入,Outlet无法识别这些子元素,自然不会渲染任何内容。
修复方案(二选一)
方案1:修改Access组件,直接渲染传入的子元素
将Access组件的返回值从<Outlet />改为传入的children,适配当前路由写法:
import { Navigate } from "react-router-dom"; const Access = ({ role, children }) => { const userRole = sessionStorage.getItem("role"); const token = sessionStorage.getItem('token'); if (!token || (role && role !== userRole)) { return <Navigate to="/login" replace />; } return children; }; export default Access
方案2:改用嵌套路由结构(符合react-router-dom v6规范)
调整App.js的路由配置,将Access作为父路由的element,页面组件作为子路由的index元素:
function App() { return ( <div> <Routes> <Route path='/' element={<Home/>}/> <Route path='/login' element={<Login/>}/> <Route path='/signup' element={<Signup/>}/> {/* 嵌套路由方式 */} <Route path="/basic" element={<Access role={'basic'} />}> <Route index element={<Basic />} /> </Route> <Route path="/admin" element={<Access role={'admin'} />}> <Route index element={<Admin />} /> </Route> <Route path='*' element={<Error/>}/> </Routes> </div> ) }
此方案无需修改原Access组件,Outlet会自动渲染子路由的index元素。
另外注意:Admin页面的文本拼写错误(wecome应为welcome),但这不是页面空白的原因。
内容的提问来源于stack exchange,提问作者Moshe Az
相关产品推荐
相关产品推荐

