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

Node.js Passport JWT认证异常:生成Token正常但路由保护报错

解决Passport JWT认证中Cannot read property '_id' of undefined的问题

我一眼就看出问题出在JWT Payload的结构上,咱们一步步拆解解决:

问题根源

你在登录接口里用user.toJSON()生成JWT的Payload:

var token = jwt.sign(user.toJSON(), config.secret, {expiresIn: 1000 });

Mongoose的toJSON()方法会自动移除文档内部的特殊属性(比如$__、_doc这些框架私有的字段),所以最终生成的JWT Payload里根本没有$__这个对象,自然就找不到$__._id,这就是报错的核心原因。

另外,你用的ExtractJwt.fromAuthHeader()已经被passport-jwt标记为过时,建议换成更标准的提取方式,避免后续踩坑。

具体修复步骤

1. 重构JWT生成逻辑,只传递必要字段

别直接序列化整个Mongoose文档,而是提取你需要的核心用户信息(比如_id、email)来生成JWT,这样Payload结构更清晰,也不会带多余的内部属性:

// 修改后的登录接口代码
router.post('/login', function(req, res) {
  User.findOne({ email: req.body.email }, function(err, user) {
    if (err) throw err;
    if (!user) {
      res.send({ success: false, message: 'Authentication failed. User not found.' });
      console.log('User not found');
    } else {
      user.comparePassword(req.body.password, function(err, isMatch) {
        if (isMatch && !err) {
          // 手动提取需要的字段作为Payload
          const userPayload = {
            _id: user._id,
            email: user.email
          };
          var token = jwt.sign(userPayload, config.secret, {expiresIn: 1000 });
          var decoded = jwt.decode(token);
          console.log(decoded);
          // 换成标准的Bearer Token格式
          res.json({ success: true, token: 'Bearer ' + token, decoded: decoded });
          console.log('Connected : ' + token);
        } else {
          res.send({ success: false, message: 'Authentication failed. Passwords did not match.' });
          console.log('Password is wrong');
        }
      });
    }
  });
});

2. 修改Passport验证逻辑,直接读取Payload中的_id

既然我们已经把_id直接放在Payload顶层了,就不需要再去访问不存在的$__属性了:

// 修改后的passport.js代码
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var User = require('../models/User');
var config = require('./database');

module.exports = function(passport) {
  var opts = {};
  // 使用最新的Bearer Token提取方式
  opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
  opts.secretOrKey = config.secret;
  passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
    // 直接读取jwt_payload._id
    User.findById(jwt_payload._id, function(err, user) {
      if (err) {
        return done(err, false);
      }
      if (user) {
        done(null, user);
      } else {
        done(null, false);
      }
    });
  }));
};

3. 调整请求头格式(关键)

现在我们用了Bearer前缀的Token,前端请求/dashboard时,必须在请求头里设置:

Authorization: Bearer <你的Token内容>

如果你之前习惯用JWT前缀,也可以保留,但要同步修改提取方式:

opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('jwt');

额外提醒

  • 绝对不要把敏感信息(比如密码哈希)放进JWT Payload里,JWT是可解码的,内容完全公开,只能靠签名保证不被篡改。
  • 你设置的expiresIn: 1000是1000秒(约16分钟),测试没问题,生产环境可以根据需求调整时长。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:10:49