You need to enable JavaScript to run this app. NodeJS+Express使用Passport+JWT认证时Mocha测试返回401问题求助
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS+Express使用Passport+JWT认证时Mocha测试返回401问题求助

问题排查:Passport JWT认证测试返回401而非预期200

用户基于NodeJS+Express开发API,使用Passport和JWT实现接口认证功能,用Mocha配合Chai、chai-http做接口测试时,传入有效JWT请求需要认证的/team接口,预期返回200状态码,实际返回401。

测试文件代码

const passport = require ('passport');
// chai是一个第三方库,支持通过函数校验变量信息
const chai = require ('chai');

// chai插件,支持启动服务并向服务发起HTTP请求
const chaiHttp = require ('chai-http');

// 做接口测试需要引入后端入口文件app.js
// .app用于获取服务实例对象
const app = require ('../app').app;

// 调用use方法告知chai使用http插件
chai.use (chaiHttp);

describe ('Auth Test Suite', () => {

    it ('用户未授权时返回401', (done) => {

        // 告知chai使用文件中定义的服务实例
        chai.request (app)
            // 向服务发起指定类型的HTTP请求
            .get ('/team')
            // 传入回调函数处理请求返回结果
            .end ((err, res) => {
                // 校验返回的HTTP状态码符合预期
                chai.assert.equal (res.status, 401);
                // 标记测试执行完成
                done ();
            });
    });

    it ('JWT token有效时返回200', (done) => {
        // 告知chai使用文件中定义的服务实例
        chai.request (app)
            // 向服务发起指定类型的HTTP请求
            .post ('/login')
            // 传入回调函数处理请求返回结果
            .end ((err, res) => {
                // 登录请求拿到token后,再次发起get请求校验token有效性
                chai.request (app)
                    .get ('/team')
                    // 传入请求头用于JWT校验
                    .set ('Authorization', `jwt ${res.body.token}`)
                    .end ((err, res) => {
                        // token有效时预期返回200
                        chai.assert.equal (res.status, 200);
                        // 标记测试执行完成
                        done ();
                    });
            });
    });
});

app.js代码

// 导入express库
const express = require ('express');

// 导入passport库
const passport = require ('passport');

// 定义变量承载express的功能能力
const app = express ();

// 定义express使用的监听端口
const port = 3000;

// 调用认证校验逻辑
require ('./auth') (passport);

// 调用app的listen方法启动服务监听连接,否则服务执行后会直接关闭,传入回调函数在服务启动后执行对应逻辑
app.listen (port, () => {
    console.log ('Server started on port' + port);
})

// 挂载get请求处理逻辑,"req" 代表请求对象,"res" 代表响应对象
app.get ('/', (req, res) => {
    // 调用".send"方法向客户端返回请求响应
    res.status (200) .send ('hello world');
});

app.post ('/login', (req, res) => {
    res.status (200) .json (
        {token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0YjoxNTE2MjMXWF0IjoxNTE2MjMXWG2DMXM02MjMXH0IjoxNTE2MjMXWG2DMXM02MJMXH0IjoxNTE2MjMXMXWG2DMXMJ2MJMX2DXMXM02MjWG'
    )
});


app.get ('/team',
    // 访问该路由时会校验请求方是否持有有效JWT,为中间件
    passport.authenticate ('jwt', {session: false}),
    (req, res) => {
        res.status (200) .send ('hello world');
    });

app.post ('/team/pokemons', (req, res) => {
    res.status (200) .send ('hello world');
});

app.delete ('/team/pokemons/:pokeid', (req, res) => {
    res.status (200) .send ('hello world');
});

app.put ('/team', (req, res) => {
    res.status (200) .send ('hello world');
});

module.exports.app = app;

auth.js(Passport JWT配置)代码

// 导入passport相关库,Strategy用于创建认证策略
const jwtStrategy = require ('passport-jwt'). Strategy;

const extractJwt = require ('passport-jwt'). ExtractJwt;

// 导出接收passport作为参数的函数
module.exports = passport => {
     // 创建包含认证策略配置的JSON对象
     const opts = {
         // 指定请求中JWT的提取位置
         jwtFromRequest: extractJwt.fromAuthHeaderWithScheme ("JWT"),
         secretOrKey: 'secretPassword'
     }
     // 告知passport使用上述创建的策略,传入回调函数处理校验结果,执行自定义校验逻辑
     passport.use (new jwtStrategy (opts, (decoded, done) => {
         console.log ('decoded jwt', decoded);
         return done (null, false);
     }));
}
问题原因与修复方案
  • 核心错误:JWT验证回调直接返回认证失败
    auth配置中,passport-jwt的验证回调第二个参数传入false,代表直接判定认证不通过,这是401的直接原因。Passport要求验证通过时需要在第二个参数传入用户身份信息,而非false。
    修复:将return done (null, false);修改为return done (null, decoded);
  • 次要错误:JWT令牌本身非法
    login接口返回的token是无效拼接值,不符合JWT三段式格式规范,无法通过密钥校验。需要使用标准方法生成合法JWT:
    首先安装JWT生成依赖:npm install jsonwebtoken
    修改login接口代码:
const jwt = require('jsonwebtoken');
app.post ('/login', (req, res) => {
    const token = jwt.sign(
        {userId: 1, username: 'testUser'},
        'secretPassword',
        {expiresIn: '1h'}
    )
    res.status(200).json({token: token})
});
  • 可选优化:Authorization头格式统一
    测试中使用小写jwt作为令牌前缀,配置中使用大写JWT作为scheme,虽然fromAuthHeaderWithScheme本身大小写不敏感,统一为JWT ${res.body.token}可以避免潜在兼容性问题。

修复后重新运行测试即可返回预期200状态码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 21:45:00