You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何转发请求头JWT并与Cookie中JWT对比完成用户身份认证?

问题修复方案

一、后端侧修复

1. 前置依赖校验

确保已安装并注册cookie-parser中间件,否则Node服务无法读取请求携带的Cookie:

npm i cookie-parser

在入口文件app.js中注册中间件:

const cookieParser = require('cookie-parser');
app.use(cookieParser());

2. 修改权限校验中间件

你当前的protect中间件仅读取请求头的Authorization字段,未读取Cookie中的JWT,也没有做两者的一致性校验,修改对应逻辑:

exports.protect = catchAsync(async (req, res, next) => {
  // 1) 同时从请求头和Cookie获取token
  let headerToken, cookieToken, token;
  if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
    headerToken = req.headers.authorization.split(' ')[1];
    token = headerToken;
  }
  // 新增读取Cookie中的jwt
  if (req.cookies.jwt) {
    cookieToken = req.cookies.jwt;
    // 若请求头无token则默认用Cookie的token
    if (!token) token = cookieToken;
  }
  // 你要求的请求头与Cookie token一致性校验
  if (headerToken && cookieToken && headerToken !== cookieToken) {
    return next(new AppError('身份凭证不一致,请重新登录', 401));
  }
  if (!token) {
    return next(
      new AppError('You are not logged in! Please log in to get access.', 401)
    );
  }
  // 后续原有校验逻辑保持不变
  // 2) Verification token
  const decoded = await promisify(jwt.verify)(
    token,
    'my-ultra-secure-and-ultra-long-secret'
  );
  // 3) Check if user still exists
  const currentUser = await User.findById(decoded.id);
  if (!currentUser) {
    return next(
      new AppError(
        'The user belonging to this token does no longer exist.',
        401
      )
    );
  }
  // 4) Check if user changed password after the token was issued
  if (currentUser.changedPasswordAfter(decoded.iat)) {
    return next(
      new AppError('User recently changed password! Please log in again.', 401)
    );
  }
  // GRANT ACCESS TO PROTECTED ROUTE
  req.user = currentUser;
  res.locals.user = currentUser;
  next();
});

注意:你路由中用到的isLoggedIn中间件也要同步修改token读取逻辑,和上面保持一致。


二、前端侧修复

1. 持久化登录状态解决刷新丢失问题

Redux的状态存储在内存中,页面刷新会被清空,需要将登录信息持久化到本地存储,修改登录action:

export const login = (email, password) => async dispatch => {
  try {
    dispatch({
      type: USER_LOGIN_REQUEST,
    });

    const config = {
      headers: {
        "Content-Type": "application/json",
      },
    };

    const { data } = await axios.post(
      '/api/v1/users/login',
      { email, password },
      config
    );

    dispatch({
      type: USER_LOGIN_SUCCESS,
      payload: data,
    });
    // 新增:持久化存储登录信息到localStorage
    localStorage.setItem('userInfo', JSON.stringify(data));
   
  } catch (error) {
    dispatch({
      type: USER_LOGIN_FAIL,
      payload:
        error.response && error.response.data.message
          ? error.response.data.message
          : error.message,
    });
  }
};

2. 全局配置请求头自动携带token

新增axios请求拦截器,所有请求自动在请求头带上JWT,不需要每次请求单独配置:

// 在项目公共axios配置文件中添加
import axios from 'axios';
// 跨域请求携带Cookie必须开启该配置
axios.defaults.withCredentials = true;

axios.interceptors.request.use(
  config => {
    const userInfo = JSON.parse(localStorage.getItem('userInfo'));
    if (userInfo?.token) {
      config.headers.Authorization = `Bearer ${userInfo.token}`;
    }
    return config;
  },
  error => Promise.reject(error)
);

3. 应用初始化恢复登录状态

在入口文件App.js中添加初始化逻辑,页面刷新后从本地存储读取登录信息恢复Redux状态:

import { useDispatch } from 'react-redux';

function App() {
  const dispatch = useDispatch();
  useEffect(() => {
    const userInfo = JSON.parse(localStorage.getItem('userInfo'));
    if (userInfo) {
      dispatch({
        type: USER_LOGIN_SUCCESS,
        payload: userInfo
      });
    }
  }, [dispatch])

  // 其余App逻辑保持不变
}

4. 可选:登出逻辑补充

登出时需要同步清空本地存储和后端Cookie:

export const logout = () => dispatch => {
  localStorage.removeItem('userInfo');
  dispatch({ type: USER_LOGOUT });
  // 调用后端登出接口清空jwt Cookie
  axios.get('/api/v1/users/logout');
};

内容的提问来源于stack exchange,提问作者valkkorn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 01:27:04