React V6中ProtectedRoutes无法保护/auth路径,请求排查解决
问题:React Router V6中ProtectedRoutes无法正确保护/auth路径
在React Router V6中使用ProtectedRoutes组件保护/auth路径,但未登录状态下仍能直接访问该页面,确认问题与V5到V6的路由变更相关,相关代码如下:
index.js
import React from 'react'; import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; import 'bootstrap/dist/css/bootstrap.min.css'; import { BrowserRouter } from "react-router-dom"; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <BrowserRouter> <App /> </BrowserRouter> </React.StrictMode> );
App.js
import { Routes, Route } from "react-router-dom"; import { Container, Col, Row } from "react-bootstrap"; import Account from "./Account"; import FreeComponent from "./FreeComponent"; import AuthComponent from "./AuthComponent"; import ProtectedRoutes from "./ProtectedRoutes"; function App() { return ( <Container> <Row> <Col className="text-center"> <h1>React Authentication Tutorial</h1> <section id="navigation"> <a href="/">Home </a> <a href="/free">Free Component </a> <a href="/auth">Auth Component </a> </section> </Col> </Row> <Routes> <Route exact path="/" element={<Account />} /> <Route exact path="/free" element={<FreeComponent />} /> <Route element={<ProtectedRoutes />}> <Route exact path="/auth" element={<AuthComponent/>}/> </Route> </Routes> </Container> ); } export default App;
ProtectedRoutes.js
import React from 'react' import {Navigate, Outlet} from 'react-router-dom'; import Cookies from "universal-cookie"; const cookies = new Cookies(); const useAuth=()=>{ const token = cookies.get("TOKEN"); if(token){ return true } else { return false } } const ProtectedRoutes=(props:any) =>{ const auth=useAuth() return auth?<Outlet/>: <Navigate to="/"/> } export default ProtectedRoutes;
AuthComponent.js
import React, { useEffect, useState } from "react"; import { Button } from "react-bootstrap"; import axios from "axios"; import Cookies from "universal-cookie"; const cookies = new Cookies(); const token = cookies.get("TOKEN"); export default function AuthComponent() { const [message, setMessage] = useState(""); useEffect(() => { const configuration = { method: "get", url: "https://myurl.com/auth-endpoint", headers: { Authorization: `Bearer ${token}`, }, }; axios(configuration) .then((result) => { setMessage(result.data.message); }) .catch((error) => { error = new Error(); }); }, []); const logout = () => { cookies.remove("TOKEN", { path: "/" }); window.location.href = "/"; } return ( <div className="text-center"> <h1>Auth Component</h1> <h3 className="text-danger">{message}</h3> <Button type="submit" variant="danger" onClick={() => logout()}> Logout </Button> </div> ); }
问题表现:访问http://localhost:3000/auth时无需登录即可查看页面,需实现登录后才能访问该路径的效果。
解决方案
1. 修正useAuth为符合规范的自定义Hook
原来的useAuth是普通函数,未遵循React Hook规则,导致组件无法在认证状态变化时重新渲染。修改后确保它是合法的自定义Hook:
修改后的ProtectedRoutes.js:
import React from 'react' import { Navigate, Outlet } from 'react-router-dom'; import Cookies from "universal-cookie"; // 自定义Hook必须以use开头,符合React Hook规则 const useAuth = () => { const cookies = new Cookies(); const token = cookies.get("TOKEN"); // 用!!将token转换为布尔值,避免空字符串、null等被误判为有效 return !!token; } const ProtectedRoutes = () => { const isAuthenticated = useAuth(); // 使用replace属性,避免用户通过后退按钮回到/auth页面 return isAuthenticated ? <Outlet /> : <Navigate to="/" replace />; } export default ProtectedRoutes;
2. 增强AuthComponent的认证校验
在AuthComponent初始化时主动校验token有效性,处理token不存在或过期的情况:
修改后的AuthComponent.js:
import React, { useEffect, useState } from "react"; import { Button } from "react-bootstrap"; import axios from "axios"; import Cookies from "universal-cookie"; export default function AuthComponent() { const [message, setMessage] = useState(""); const cookies = new Cookies(); useEffect(() => { const token = cookies.get("TOKEN"); // 先检查本地token是否存在,不存在直接跳转登录页 if (!token) { window.location.href = "/"; return; } const configuration = { method: "get", url: "https://myurl.com/auth-endpoint", headers: { Authorization: `Bearer ${token}`, }, }; axios(configuration) .then((result) => { setMessage(result.data.message); }) .catch((error) => { // API请求失败(如token过期),清除无效token并跳转登录页 cookies.remove("TOKEN", { path: "/" }); window.location.href = "/"; }); }, []); const logout = () => { cookies.remove("TOKEN", { path: "/" }); window.location.href = "/"; } return ( <div className="text-center"> <h1>Auth Component</h1> <h3 className="text-danger">{message}</h3> <Button type="submit" variant="danger" onClick={logout}> Logout </Button> </div> ); }
3. 优化App.js的路由写法
React Router V6中exact属性已被移除(路由默认精确匹配),可以删除所有exact属性,让代码更简洁:
修改后的App.js路由部分:
<Routes> <Route path="/" element={<Account />} /> <Route path="/free" element={<FreeComponent />} /> <Route element={<ProtectedRoutes />}> <Route path="/auth" element={<AuthComponent/>}/> </Route> </Routes>
内容的提问来源于stack exchange,提问作者Karina
相关产品推荐
相关产品推荐

