React路由变更时如何触发登出并校验JWT accessToken是否过期
如何在JWT accessToken过期时对用户执行登出操作?
需求背景
- 现有的
withRouter高阶组件路由监听逻辑仅在用户点击浏览器返回按钮时生效 - 需要覆盖所有路由变更场景(包括点击页面内导航链接如「返回首页」的场景),检测到token过期时自动移除localStorage中存储的token并执行登出
原始代码
AuthVerify.js
import React, { useEffect } from "react"; import { withRouter } from "react-router-dom"; const parseJwt = (token) => { try { return JSON.parse(atob(token.split(".")[1])); } catch (e) { return null; } }; const AuthVerify = (props) => { console.log(props); useEffect(() => { props.history.listen(() => { const token = localStorage.getItem("accessToken"); if (token) { const decodedJWT = parseJwt(token); if (decodedJWT.exp * 1000 < Date.now()) { console.log("expired"); props.logOut(); } } }); }, []); return <div></div>; }; export default withRouter(AuthVerify);
App.js
import { Fragment } from "react"; import { BrowserRouter, Switch, Route } from "react-router-dom"; import AuthVerify from "../src/utils/AuthVerify"; import "./App.css"; import Home from "./pages/Home"; import Signup from "../src/pages/Signup"; import Login from "./pages/Login"; import Test from "./pages/Test"; const logOut = () => { localStorage.removeItem("accessToken"); localStorage.removeItem("refreshToken"); }; function App() { return ( <Fragment> <div className="App"> <BrowserRouter> <Switch> <Route exact path="/" component={Home} /> <PrivateRoute exact path="/test" component={Test} /> <Route exact path="/register" component={Signup} /> <Route exact path="/login" component={Login} /> </Switch> </BrowserRouter> </div> <AuthVerify logOut={logOut} /> </Fragment> ); } export default App;
问题原因
withRouter注入的history实例依赖React Router的路由上下文,原始代码中<AuthVerify />被放在<BrowserRouter>组件外部,无法监听到通过Link/history.push等前端路由API触发的路由变更,仅能捕获浏览器前进后退触发的popstate事件。
修复方案
调整AuthVerify组件的放置位置,将其移入<BrowserRouter>内部,即可获取到全局统一的路由上下文,监听到所有路由变更事件:
import { Fragment } from "react"; import { BrowserRouter, Switch, Route } from "react-router-dom"; import AuthVerify from "../src/utils/AuthVerify"; import "./App.css"; import Home from "./pages/Home"; import Signup from "../src/pages/Signup"; import Login from "./pages/Login"; import Test from "./pages/Test"; const logOut = () => { localStorage.removeItem("accessToken"); localStorage.removeItem("refreshToken"); // 可补充登出后跳转登录页逻辑,如 window.location.href = '/login' }; function App() { return ( <Fragment> <div className="App"> <BrowserRouter> <Switch> <Route exact path="/" component={Home} /> <PrivateRoute exact path="/test" component={Test} /> <Route exact path="/register" component={Signup} /> <Route exact path="/login" component={Login} /> </Switch> {/* 移入BrowserRouter内部,获取正确的路由上下文 */} <AuthVerify logOut={logOut} /> </BrowserRouter> </div> </Fragment> ); } export default App;
可选优化
可给全局请求拦截器添加token过期校验逻辑,在请求发送前先判断token是否过期,若过期直接执行登出逻辑,覆盖路由未变更时的接口请求场景。
效果演示


内容的提问来源于stack exchange,提问作者Gourav Thakur
相关产品推荐
相关产品推荐

