[英]struggling to implement RBAC in react, pages won't render after successful login
我试图在我的反应应用程序中实现 RBAC,我使用 nodejs 作为后端,它工作得很好,并且在反应中我使用 vite 和 react-router-dom v6。 为了简单起见,我决定将从我的服务器检索到的令牌和角色存储在 Session 存储中。 我的应用程序中有 2 个角色,“基本”和“管理员”,成功登录后,我根据从服务器检索到的角色进行重定向。 我有一个 HOC 作为“中间件”,也可以检查我的路由布局中的角色权限。 重定向工作正常,但是,当我测试它时,我得到了两个角色的空白页面,它们都应该呈现一个简单的 h1 标签。我错过了什么,或者在我的实现中做错了什么?
我的登录页面:
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:
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)) {
// Redirect to login if token is not available or user role doesn't match
return <Navigate to="/login" replace />;
}
// User has the required role or no role is specified, render the nested components
return <Outlet />;
};
export default Access
我希望这两个分页都可以根据登录的角色重定向使用简单的 h1 标签呈现。
管理页面:
/* eslint-disable no-unused-vars */
import React from "react"
const Admin = () => {
return (
<div>wecome to admin page</div>
)
}
export default Admin
// eslint-disable-next-line no-unused-vars
import React from 'react'
const Basic = () => {
return (
<div>welcome to basic page</div>
)
}
export default Basic
经过大量调整后,我认为我终于能够解决这个问题。我创建了一个 HOC 来接受角色,然后有条件地导航当前登录。
这是我的解决方案,但我会保持这篇文章开放,以防有人有比我更好的解决方案。
让我们从 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 BasicSubPage from "./pages/BasicSubPage";
import BasicSecondPage from "./pages/BasicSecondPage";
import PublicNav from "./pages/PublicNav";
import Error from "./pages/Error";
function App() {
return (
<div>
<PublicNav/>
<Routes>
{/* public routes */}
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signup />} />
{/* basic protected routes */}
<Route element={<Access role={"basic"}/>}>
<Route path="/basic" element={<Basic/>}>
<Route path="basicsub" element={<BasicSubPage/>}/>
<Route path="/basic/basicsecond" element={<BasicSecondPage/>}/>
</Route>
</Route>
{/* admin protected routes */}
<Route element={<Access role={"admin"}/>}>
<Route path="/admin" element={<Admin />} />
</Route>
<Route path="/error" element={<Error />} />
</Routes>
</div>
);
}
export default App;
如您所见,我的 HOC 被命名为“Access”,它接受一个角色作为 prop。
这是“访问”组件:请注意:这是一个简单的验证,因为我想让事情尽可能简单。
import { Navigate, Outlet } from "react-router-dom";
const Access = ({role}) => {
const token = sessionStorage.getItem("token");
const userRole = sessionStorage.getItem("role");
if(!token || token && !role.includes(userRole)) {
return <Navigate to="/error" replace/>
}
return <div>
<Outlet/>
</div>;
}
export default Access
我的登录组件保持不变,所以那里没有变化,我添加到“基本”用户的子页面,所以当我可以使用“选项卡”布局在这两个页面之间导航时..
所以这是我的“基本”页面,请注意其他页面呈现了简单的 h1 标签,所以我没有在此处添加它们。
基本页面:
// eslint-disable-next-line no-unused-vars
import { useEffect, useState } from "react";
import { getInfo } from "../utils/basicUtils";
import { useNavigate , NavLink, Outlet} from "react-router-dom";
const Basic = () => {
const navigate = useNavigate();
const token = sessionStorage.getItem("token");
const [personalData, setPersonalData] = useState({});
useEffect(() => {
if (!token) {
sessionStorage.removeItem('token');
sessionStorage.removeItem('role');
navigate(0);
}
const getPersonalInfo = async () => {
const url = 'http://localhost:8080/basic/info'
const resp = await getInfo(`${url}`,token);
if(resp.status === 403) {
console.log("forbiden");
sessionStorage.removeItem('token');
sessionStorage.removeItem('role');
navigate(0);
navigate('/home');
}
setPersonalData(resp.data.personalInfo)
console.log("success", resp.data.personalInfo);
};
getPersonalInfo()
}, [token]);
const logout = () => {
sessionStorage.removeItem('token');
sessionStorage.removeItem('role');
navigate('/home')
}
return (
// <NavLink className="Link" to="/home">Home</NavLink>
<div>
<h1>welcome {personalData.name}</h1>
<button onClick={logout}>Logout</button>
<nav>
<NavLink to={"basicsub"}>basic-sub</NavLink>
<NavLink to={"/basic/basicsecond"}>basic-sub-second</NavLink>
</nav>
<Outlet/>
</div>
);
};
export default Basic;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.