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

MERN项目中passport-jwt授权失败问题求助

MERN项目中passport-jwt授权失败排查

问题概述

在MERN项目中使用passport-jwt实现身份验证时,向/profile路由发送包含Bearer <generated-token>授权头的GET请求,始终返回"unauthorized"响应。尝试过ExtractJwt.fromAuthHeaderWithScheme('jwt')等多种token提取方式,问题仍未解决。

依赖版本

"cors": "2.8.5",
"dotenv": "16.0.3",
"express": "4.18.2",
"jsonwebtoken": "9.0.0",
"mongoose": "7.0.4",
"nodemon": "2.0.22",
"passport": "0.6.0",
"passport-jwt": "4.0.1"

相关代码

server/config/passport.js

opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = process.env.SECRET_KEY;

passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
    User.findOne({ _id: jwt_payload.sub })
        .then(user => {
            if (user) {
                return done(null, user);
            } else {
                return done(null, false);
            }
        })
        .catch(err => {
            return done(err, false);
        });
}));

server/app.js路由代码

//login route
app.post("/login", async (req,res) =>{
    const user = await User.findOne({phone : req.body.phone});
    if(!user){
        res.status(400).send("User not found!");
    }
    if(!bcrypt.compareSync(req.body.password, user.password)){
        res.status(400).send("Incorrect password!");
    }

    const payload = {
        id: user._id,
        phone: user.phone
    }
    const token = jwt.sign(payload, process.env.SECRET_KEY, {expiresIn: "2d"});

    return  res.status(200).send({ success : true, message : "Log in successful", token : "Bearer "+token});
})

//profile route
app.get('/profile', passport.authenticate('jwt', { session: false }),
    function(req, res) {
        res.status(200).send({
            message: "Tomar account", 
            token: "Bearer "+token,
            user: {
                id: req.user._id,
                phone: req.user.phone
            }
        }); 
    }
);

排查与解决方向

1. JWT Payload字段不匹配(核心问题)

生成JWT时,payload里定义的是id: user._id,但在passport的JwtStrategy中,查询条件用的是jwt_payload.sub——默认情况下jsonwebtoken不会自动将id映射为sub字段,导致查询不到用户,直接返回未授权。

解决方法二选一:

  • 修改login路由的payload,将用户ID存入sub字段:
    const payload = {
        sub: user._id, // 替换原来的id
        phone: user.phone
    }
    
  • 或者修改passport配置中的查询条件,使用payload里的id字段:
    User.findOne({ _id: jwt_payload.id })
    

2. 环境变量加载顺序问题

确保dotenv在passport配置文件加载前完成初始化。如果server/config/passport.js先于dotenv.config()加载,会导致process.env.SECRET_KEY为空,JWT验证失败。

修正方式:在app.js最顶部先加载dotenv:

require('dotenv').config();
// 再引入passport配置
require('./config/passport');

3. Login路由错误处理漏洞

当前login路由中,当用户不存在或密码错误时,只发送响应但未终止后续代码执行,可能导致逻辑混乱(比如后续仍会生成token)。需要在错误响应后添加return:

if(!user){
    return res.status(400).send("User not found!");
}
if(!bcrypt.compareSync(req.body.password, user.password)){
    return res.status(400).send("Incorrect password!");
}

4. Profile路由变量未定义问题

Profile路由中尝试返回token变量,但该变量仅在login路由中定义,会导致服务器500错误。需要移除该字段,或者从请求头中获取并返回:

res.status(200).send({
    message: "Tomar account", 
    user: {
        id: req.user._id,
        phone: req.user.phone
    }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 11:22:21