使用React+Express+JWT技术栈时 应在哪里调用接口验证用户登录状态
基于React+Express+JWT的登录状态校验实现方案
第一步:后端(Express)接口实现
首先需要实现JWT校验中间件和登录状态校验接口,核心逻辑在校验JWT的签名有效性、是否过期,同时支持识别已吊销的JWT:
// 提前安装依赖:npm install jsonwebtoken cookie-parser const jwt = require('jsonwebtoken'); const cookieParser = require('cookie-parser'); const express = require('express'); const app = express(); app.use(cookieParser()); // JWT密钥,必须和签发登录token时使用的密钥完全一致 const JWT_SECRET = process.env.JWT_SECRET || 'your_custom_secret_key'; // 通用JWT校验中间件 const authMiddleware = (req, res, next) => { const token = req.cookies.jwt; if (!token) { return res.status(401).json({ isLoggedIn: false, message: '未携带登录凭证' }); } try { // 校验JWT的签名和有效期 const decoded = jwt.verify(token, JWT_SECRET); req.user = decoded; // 解析出的用户信息挂载到请求对象上 next(); } catch (err) { return res.status(401).json({ isLoggedIn: false, message: '登录状态已失效' }); } }; // 登录状态校验专用接口 app.get('/api/auth/check', authMiddleware, (req, res) => { res.json({ isLoggedIn: true, userInfo: { id: req.user.id, username: req.user.username // 按需返回其他用户字段 } }); });
注意:签发JWT时建议把jwt存入HttpOnly类型的Cookie,防止XSS攻击窃取凭证,示例配置如下:
res.cookie('jwt', token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', // 生产环境HTTPS下开启 maxAge: 7 * 24 * 60 * 60 * 1000 // 有效期和JWT本身的有效期保持一致 });
第二步:前端(React)路由改造
原来的同步判断Cookie逻辑需要改成异步校验,增加加载状态避免页面闪跳:
import { Route, Redirect } from "react-router-dom"; import { useState, useEffect } from "react"; import axios from "axios"; export const PrivateRoute = ({ children, ...rest }) => { // 校验中状态、登录状态 const [loading, setLoading] = useState(true); const [isLoggedIn, setIsLoggedIn] = useState(false); useEffect(() => { const checkAuthStatus = async () => { try { // withCredentials必须开启,才能携带Cookie请求后端接口 const res = await axios.get('/api/auth/check', { withCredentials: true }); setIsLoggedIn(res.data.isLoggedIn); // 此处可将返回的用户信息存入全局状态(Context/Redux/Zustand等)供其他组件使用 } catch (err) { setIsLoggedIn(false); } finally { setLoading(false); } }; checkAuthStatus(); }, []); // 校验过程中显示加载态,避免登录/目标页闪跳 if (loading) return <div>加载中...</div>; return ( <Route {...rest} render={({ location }) => isLoggedIn ? ( children ) : ( <Redirect to={{ pathname: "/login", state: { from: location } }} /> ) } /> ); };
如果使用React Router v6,只需要把返回的Route组件替换为Outlet即可,校验逻辑完全一致。
可优化的细节
- 增加全局状态缓存:把校验后的登录状态和用户信息存入全局状态中,路由切换时直接读缓存,只有页面刷新时才重新调用校验接口,减少不必要的请求
- 全局响应拦截:给axios加响应拦截器,只要任意业务接口返回401状态码,直接清除本地登录状态跳转到登录页,避免状态不同步
- 避免loading闪屏:可以给加载状态加100ms以内的防抖,请求很快返回的话不显示loading组件,体验更好
核心逻辑说明
不推荐前端直接校验JWT的原因是:前端只能校验JWT的格式和有效期,无法识别已被后端吊销的凭证(比如用户主动登出、账号被封禁的场景),后端统一校验安全性更高。
接口调用的最佳位置是PrivateRoute路由守卫中统一处理,所有需要登录的路由都会自动走校验逻辑,不需要每个业务页面单独实现。
内容的提问来源于stack exchange,提问作者Augustin
相关产品推荐
相关产品推荐

