React受保护路由Token验证通过后无法跳转至Admin首页求助
React受保护路由验证通过后仍重定向至登录页问题
Token验证功能可正常检测有效性,但验证通过后仍被重定向至Login页面,无法进入预期的Admin首页。尝试在ProtectedRoute组件中使用<Outlet/>未生效,控制台显示Token验证完全正常,怀疑App.js中的路由配置存在问题,相关代码如下:
ProtectedRoute.js
import { Navigate } from 'react-router-dom'; import Cookies from 'js-cookie'; import { checkToken } from './auth'; import Admin from '../pages/Admin-page/Admin'; import { useState, useEffect } from 'react'; const ProtectedRoute = () => { const [isLoading, setIsLoading] = useState(true); const [isValid, setIsValid] = useState(false); useEffect(() => { const verifyToken = async () => { try { const valid = await checkToken(); console.log('Token valid:', valid); // Debugging setIsValid(valid); } catch (error) { console.error('Error validating token:', error); setIsValid(false); } finally { setIsLoading(false); } }; verifyToken(); }, []); if (isLoading) { return <div>Loading...</div>; } console.log('Token isvalid:', isValid); // Debugging return isValid ? <Admin /> : <Navigate to="/login" replace />; }; export default ProtectedRoute;
App.js
import React from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import Login from './authentications/Login'; import Users from './pages/Side-menu/Users'; import Hours from './pages/Side-menu/Hours'; import Tenants from './pages/Side-menu/Tenants'; import ResetPassword from './authentications/ResetPassword'; import NewPassword from './authentications/NewPassword'; import ProtectedRoute from './authentications/ProtectedRoute'; import PostContent from './pages/Admin-page/PostContent'; import Admin from './pages/Admin-page/Admin'; import Home from './pages/Side-menu/Home'; import Signup from './authentications/Signup'; function App() { return ( <Router> <Routes> <Route path="/login" element={<Login />} /> <Route path="/signup" element={<Signup />} /> <Route path="/forgotpassword" element={<ResetPassword />} /> <Route path="/newpassword" element={<NewPassword />} /> {/* Protected routes */} <Route path="/" element={ <ProtectedRoute> <Admin /> </ProtectedRoute> } > <Route path="admin/posts" element={<PostContent />} /> <Route path="admin/users" element={<Users />} /> <Route path="admin/hours" element={<Hours />} /> <Route path="admin/tenants" element={<Tenants />} /> </Route> </Routes> </Router> ); } export default App;
Login.js
import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useTranslate } from '@tolgee/react'; import { LanguageSelect } from '../LanguageSelect'; import { checkToken } from './auth'; import axios from 'axios'; import Cookies from 'js-cookie'; function Login() { const [formData, setFormData] = useState({ email: '', password: '', }); const [errors, setErrors] = useState({ password: '' }); const navigate = useNavigate(); const { t } = useTranslate(); const handleInputChange = (e) => { const { name, value } = e.target; setFormData({ ...formData, [name]: value }); }; const validatePassword = (password) => { const regex = /^(?=.*[A-Z])(?=.*\d)[A-Za-z\d@#$%^&+=!]{8,}$/; // Updated regex return regex.test(password); }; const handleLogin = async (e) => { e.preventDefault(); if (!validatePassword(formData.password)) { setErrors({ password: 'Password must be at least 8 characters long, contain at least one uppercase letter and one number.', }); return; } try { const response = await axios.post( 'https://MYWEBSITE/api/v1/users/login', { email: formData.email, password: formData.password, }, { headers: { 'Content-Type': 'application/json', }, withCredentials: true, // Added withCredentials setting } ); if (response.status === 200) { console.log('Login successful.'); setFormData({ email: '', password: '', }); const isValidToken = await checkToken(); if (isValidToken) { console.log('Token is valid, navigating to homepage.'); navigate('/'); // Redirect after successful login } else { console.error('Invalid token after login.'); } } else { console.error('Error logging in:', response.statusText); } } catch (error) { console.error('Axios error:', error); } }; return ( {/* 组件渲染内容未在代码中提供 */} ); } export default Login;
auth.js
import axios from 'axios'; export const checkToken = async () => { try { const response = await axios.get( 'https://MYWEBSITE/api/v1/users/checkToken', { headers: { 'Content-Type': 'application/json', }, withCredentials: true, } ); return response.data.status === 'success'; } catch (error) { if (error.response) { console.error('Server error:', error.response.data); return false; } else if (error.request) { console.error('Network error:', error.request); return false; } else { console.error('Request error:', error.message); return false; } } };
问题根源与修复方案
1. ProtectedRoute未正确处理嵌套路由
当前ProtectedRoute直接返回<Admin/>,既忽略了传入的子组件,也未使用<Outlet/>渲染嵌套路由,导致路由结构失效。
修改后的ProtectedRoute.js
import { Navigate, Outlet } from 'react-router-dom'; import { useState, useEffect } from 'react'; import { checkToken } from './auth'; const ProtectedRoute = () => { const [isLoading, setIsLoading] = useState(true); const [isValid, setIsValid] = useState(false); useEffect(() => { const verifyToken = async () => { try { const valid = await checkToken(); console.log('Token valid:', valid); setIsValid(valid); } catch (error) { console.error('Error validating token:', error); setIsValid(false); } finally { setIsLoading(false); } }; verifyToken(); }, []); if (isLoading) { return <div>Loading...</div>; } // 验证通过时渲染嵌套路由的Outlet,否则重定向到登录页 return isValid ? <Outlet /> : <Navigate to="/login" replace />; };
2. App.js路由配置错误
原配置将ProtectedRoute与Admin组件混合使用,未正确建立父-子路由关系。需调整为用ProtectedRoute作为路由保护层,Admin作为父组件承载子路由。
修改后的App.js
import React from 'react'; import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; import Login from './authentications/Login'; import Users from './pages/Side-menu/Users'; import Hours from './pages/Side-menu/Hours'; import Tenants from './pages/Side-menu/Tenants'; import ResetPassword from './authentications/ResetPassword'; import NewPassword from './authentications/NewPassword'; import ProtectedRoute from './authentications/ProtectedRoute'; import PostContent from './pages/Admin-page/PostContent'; import Admin from './pages/Admin-page/Admin'; import Home from './pages/Side-menu/Home'; import Signup from './authentications/Signup'; function App() { return ( <Router> <Routes> <Route path="/login" element={<Login />} /> <Route path="/signup" element={<Signup />} /> <Route path="/forgotpassword" element={<ResetPassword />} /> <Route path="/newpassword" element={<NewPassword />} /> {/* 受保护路由组:ProtectedRoute作为统一验证层 */} <Route element={<ProtectedRoute />}> {/* 默认路由指向Admin首页 */} <Route path="/" element={<Admin />} /> {/* 嵌套子路由:若Admin组件内部包含<Outlet/>,可直接嵌套 */} <Route path="admin/posts" element={<Admin><PostContent /></Admin>} /> <Route path="admin/users" element={<Admin><Users /></Admin>} /> <Route path="admin/hours" element={<Admin><Hours /></Admin>} /> <Route path="admin/tenants" element={<Admin><Tenants /></Admin>} /> </Route> </Routes> </Router> ); } export default App;
若Admin组件内部已集成<Outlet/>用于渲染子内容,可进一步简化路由配置:
<Route element={<ProtectedRoute />}> <Route path="/" element={<Admin />}> <Route path="admin/posts" element={<PostContent />} /> <Route path="admin/users" element={<Users />} /> <Route path="admin/hours" element={<Hours />} /> <Route path="admin/tenants" element={<Tenants />} /> </Route> </Route>
3. 额外注意事项
- 确保ProtectedRoute组件导入了
useState和useEffect,原代码缺失该导入会导致组件报错。 - 登录后跳转的
/路径现在会匹配受保护路由的默认规则,直接渲染Admin组件。
内容的提问来源于stack exchange,提问作者Diana Petrovčíková
相关产品推荐
相关产品推荐

