React集成ASP.NET API:登录权限控制及页面跳转问题求助
React路由权限控制问题:未登录用户仍能访问私有页面
我正在开发一款集成ASP.NET API的React应用,需求是未登录用户仅能访问登录页面,无法进入其他页面。我编写了如下代码,但未达到预期效果:
import React, { Fragment, useContext, useEffect, useState } from 'react'; import axios from 'axios'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; import SignIn from '../pages/SignIn'; import Home from '../pages/Home' import CadastroEmpresa from '../pages/CadastroEmpresa'; import CadastroIntegrante from '../pages/CadastroIntegrante'; import Empresas from '../pages/Empresas'; import useAuth from '../hooks/useAuth'; import { LoginContext } from '../context/auth'; const Private = ({ Item }) => { const signed = useAuth(); return signed > 0 ? <Item /> : <SignIn />; } export { Private } const RoutesApp = () => { return ( <BrowserRouter> <Fragment> <Routes> <Route path='/' element={<SignIn />} /> <Route path="*" element={<SignIn />} /> <Route exact path='/home' element={<Private Item={Home} />} /> <Route exact path='/empresas' element={<Private Item={Empresas} />} /> <Route exact path='/cadastro-empresa' element={<Private Item={CadastroEmpresa} />} /> <Route exact path='/cadastro-funcionario' element={<Private Item={CadastroIntegrante} />} /> </Routes> </Fragment> </BrowserRouter> ) } export default RoutesApp;
useAuth()是我基于以下Context实现的自定义Hook:
import React, { createContext, useState } from 'react'; import axios from 'axios'; const LoginContext = createContext(); const LoginProvider = ({ children }) => { const [loginData, setLoginData] = useState({ status: '', token: '' }); const validateLogin = (loginUser) => { if (!loginUser.email || !loginUser.senha) { return false; } return true; }; const passwordSignIn = async (email, senha) => { try { const response = await axios.post('https://localhost:7223/v1/entrar', { email, senha }); console.log(response.data) return response.data; } catch (error) { console.error(error); return {}; } }; const generateToken = async (email, senha) => { try { const response = await axios.post('https://localhost:7223/v1/entrar', { email, senha }); return response.data.token; } catch (error) { console.error(error); return ''; } }; const handleLogin = async (loginUser) => { if (!validateLogin(loginUser)) { return setLoginData({ status: 'error', token: '' }); } const result = await passwordSignIn(loginUser.email, loginUser.senha); if (result.succeeded) { const token = await generateToken(loginUser.email); return setLoginData({ status: 'success', token: '' }); } if (result.isLockedOut) { return setLoginData({ status: 'locked', token: '' }); } console.log(loginUser.email) console.log(loginUser.senha) return setLoginData({ status: 'error', token: '' }); }; return ( <LoginContext.Provider value={{ loginData, handleLogin }}> {children} </LoginContext.Provider> ); }; export { LoginContext, LoginProvider };
问题分析
- useAuth Hook实现缺失:仅提及基于LoginContext,但未提供具体实现,导致
Private组件中signed > 0的判断无有效依据,无法正确识别登录状态。 - 登录逻辑错误:
handleLogin中登录成功后错误将token设为空;generateToken重复调用登录接口,完全可复用passwordSignIn返回的token,无需重复请求。 - 私有路由判断逻辑不合理:用
signed > 0判断登录状态不符合业务逻辑,应基于loginData的token是否存在或status是否为success来判断。 - 路由顺序错误:通配符路由(
path="*")放在私有路由前会拦截所有后续路由,导致私有路由永远无法匹配。 - 未处理路由跳转:未登录访问私有路由时直接渲染
SignIn但不修改URL,刷新页面后路径仍为私有路由,体验不佳。
解决方案
1. 完善useAuth自定义Hook
创建正确读取LoginContext的useAuth Hook:
// hooks/useAuth.js import { useContext } from 'react'; import { LoginContext } from '../context/auth'; const useAuth = () => { const { loginData } = useContext(LoginContext); // 基于token和status判断登录状态 const isAuthenticated = loginData.token && loginData.status === 'success'; return isAuthenticated; }; export default useAuth;
2. 修正LoginProvider中的登录逻辑
修复token赋值错误,移除重复请求:
// context/auth.js import React, { createContext, useState } from 'react'; import axios from 'axios'; const LoginContext = createContext(); const LoginProvider = ({ children }) => { const [loginData, setLoginData] = useState({ status: '', token: '' }); const validateLogin = (loginUser) => { if (!loginUser.email || !loginUser.senha) { return false; } return true; }; const passwordSignIn = async (email, senha) => { try { const response = await axios.post('https://localhost:7223/v1/entrar', { email, senha }); console.log(response.data) return response.data; } catch (error) { console.error(error); return {}; } }; const handleLogin = async (loginUser) => { if (!validateLogin(loginUser)) { return setLoginData({ status: 'error', token: '' }); } const result = await passwordSignIn(loginUser.email, loginUser.senha); if (result.succeeded) { // 直接使用接口返回的token return setLoginData({ status: 'success', token: result.token }); } if (result.isLockedOut) { return setLoginData({ status: 'locked', token: '' }); } console.log(loginUser.email) console.log(loginUser.senha) return setLoginData({ status: 'error', token: '' }); }; return ( <LoginContext.Provider value={{ loginData, handleLogin }}> {children} </LoginContext.Provider> ); }; export { LoginContext, LoginProvider };
3. 修改Private组件,添加路由跳转
使用Navigate组件实现未登录时的URL跳转:
import React from 'react'; import { Navigate } from 'react-router-dom'; import useAuth from '../hooks/useAuth'; const Private = ({ children }) => { const isAuthenticated = useAuth(); return isAuthenticated ? children : <Navigate to="/" replace />; }; export { Private };
4. 调整路由顺序,修正配置
将通配符路由放在最后,私有路由用Private组件包裹:
import React from 'react'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; import SignIn from '../pages/SignIn'; import Home from '../pages/Home' import CadastroEmpresa from '../pages/CadastroEmpresa'; import CadastroIntegrante from '../pages/CadastroIntegrante'; import Empresas from '../pages/Empresas'; import { Private } from './Private'; const RoutesApp = () => { return ( <BrowserRouter> <Routes> {/* 登录页 */} <Route path='/' element={<SignIn />} /> {/* 私有路由 */} <Route path='/home' element={<Private><Home /></Private>} /> <Route path='/empresas' element={<Private><Empresas /></Private>} /> <Route path='/cadastro-empresa' element={<Private><CadastroEmpresa /></Private>} /> <Route path='/cadastro-funcionario' element={<Private><CadastroIntegrante /></Private>} /> {/* 通配符路由:未匹配路径跳转到登录页 */} <Route path="*" element={<Navigate to="/" replace />} /> </Routes> </BrowserRouter> ) } export default RoutesApp;
关键说明
- 登录状态通过
token和status双重验证,确保只有成功获取有效token的用户能访问私有页面。 - 使用
Navigate组件替换直接渲染SignIn,保证URL正确跳转,避免刷新页面后路径异常。 - 路由顺序遵循“精确路由优先,通配符路由最后”原则,确保私有路由能被正确匹配。
内容的提问来源于stack exchange,提问作者Douglas Badarô
相关产品推荐
相关产品推荐

