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

登录后JWT Token无法持久化访问的问题求助

登录后JWT Token无法在后续请求中获取的问题解决

问题背景

技术栈:NodeJS + MongoDB + Express + Pug(原生JS),采用MVC结构。
核心问题:

  • 登录接口可正常返回用户信息与JWT Token,且登录时能通过jwt.verify解析Token获取用户
  • 登录后跳转页面调用loadData接口时,Token无法被读取,currentAccount返回undefined
  • Postman调用需用户权限的接口正常,但前端页面请求时:
    • req.headers无authorization字段
    • req.cookies.JWT值为undefined

关键代码片段

accountController.js - loadData & getCurrentAccount

exports.loadData = async (req, res, next) => {
    const currentAccount = await getCurrentAccount(req);
    console.log(currentAccount) // undefined
    next();
};

const getCurrentAccount = async (req) => {
    let token;
    if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
        token = req.headers.authorization.split(' ')[1];
    } else if (req.cookies.JWT) {
        token = req.cookies.JWT;
    }

    if (token) {
        const decode = await promisify(jwt.verify)(token, process.env.JWT_S);
        const currentAccount = await Account.findById(decode.id);

        return currentAccount;
    }
    return undefined; // 此处被执行
}

authController.js - login & createToken

exports.login = async (req, res, next) => {
  const { email, password } = req.body;

  if (!email || !password) {
    return next(new AppError("Email and Password required.", 400));
  }

  const account = await Account.findOne({ email }).select("+password");

  if (!account || !(await account.correctPassword(password, account.password))
  ) {
    return next(new AppError("Incorrect email or password", 401));
  }

  var token = createToken(account, 200, res);

  const accountCopy = account;
  accountCopy.password = undefined;

  res.status(200).json({
    status: 'Success',
    token,
    data: {
      account: accountCopy
    }
  });
};

const createToken = (account, statusCode, res) => {
  const token = signToken(account._id);
  const cookieOptions = {
    httpOnly: true
  };

  if (process.env.NODE_ENV === 'production') cookieOptions.secure = true;
  res.cookie('JWT', token, cookieOptions);

  return token;
};

解决方案

1. 完善Cookie属性设置

当前createToken中的Cookie缺少SameSite属性,这会导致浏览器在页面跳转或跨域场景下不携带Cookie。根据环境调整配置:

const createToken = (account, statusCode, res) => {
  const token = signToken(account._id);
  const cookieOptions = {
    httpOnly: true,
    // 开发环境用Lax保证同域请求携带Cookie,生产环境若需跨域则用None(必须配合secure: true)
    sameSite: process.env.NODE_ENV === 'production' ? 'None' : 'Lax',
    secure: process.env.NODE_ENV === 'production'
  };

  res.cookie('JWT', token, cookieOptions);
  return token;
};

注意:若设置sameSite: 'None',必须同时开启secure: true,仅适用于HTTPS环境;开发环境HTTP下使用Lax即可。

2. 确保前端请求携带Cookie

如果前端使用axios发起请求,需开启withCredentials以自动携带Cookie:

// 前端全局配置或单个请求设置
axios.defaults.withCredentials = true;

// 单个请求示例
axios.get('/api/account/loadData', { withCredentials: true })
  .then(response => { /* 处理数据 */ })
  .catch(error => { /* 处理错误 */ });

3. 检查cookie-parser中间件加载顺序

在server.js中,需确保cookie-parser在所有路由之前加载,否则req.cookies无法被解析:

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

// 先加载cookie-parser
app.use(cookieParser());

// 再加载路由
app.use('/api/account', accountRouter);
app.use('/api/auth', authRouter);

4. 验证Cookie是否被浏览器保存

打开浏览器开发者工具 → Application → Cookies → 当前域名,检查是否存在JWT Cookie:

  • 若不存在:检查Cookie的secure、sameSite设置是否符合当前环境(开发环境HTTP下secure必须为false)
  • 若存在但请求不携带:检查前端请求是否开启withCredentials,或页面跳转是否为同域名

5. 确认环境变量配置

确保process.env.NODE_ENV在开发环境被正确设置为development,避免生产环境的Cookie规则被错误应用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 02:07:03