使用 passport-jwt 验证节点 API

Posted

技术标签:

【中文标题】使用 passport-jwt 验证节点 API【英文标题】:Authenticating node API with passport-jwt 【发布时间】:2016-06-02 09:18:51 【问题描述】:

我正在尝试使用 passport-jwt 设置 JWT 身份验证。我认为我采取了正确的步骤,但测试 GET 不会成功,我不知道如何调试它。

这是我所做的:

    尽可能设置passport-jwt straight out of the doc

    var jwtOptions = 
        secretOrKey: 'secret',
        issuer: "accounts.examplesoft.com",  // wasn't sure what this was, so i left as defaulted in the doc
        audience: "yoursite.net"   // wasn't sure what this was, so i left as defaulted in the doc
      ;
    
    jwtOptions.jwtFromRequest = ExtractJwt.fromAuthHeader();
    
    passport.use(new JwtStrategy(jwtOptions, function(jwt_payload, done) 
      User.findOne(id: jwt_payload.sub, function(err, user) 
        if (err) 
            return done(err, false);
        
        if (user) 
            done(null, user);
         else 
            done(null, false);
            // or you could create a new account
        
      );
    ));
    

    向我的用户 /login 端点添加了令牌结果

    var jwt = require('jsonwebtoken');
    // ...
    
    exports.postLogin = function(req, res, next) 
      passport.authenticate('local', function(err, user, info) 
        if (err) throw err;
        if (!user) 
            return res.send( msg: 'Login incorrect' );
        
        req.logIn(user, function(err) 
            if (err) throw err;
            var secretOrKey = jwtOptions.secretOrKey;
            var token = jwt.sign(user, secretOrKey, 
                expiresIn: 631139040 // 20 years in seconds
            );
            res.send( user: user, jwtToken: "JWT " + token );
        );
      )(req, res, next);
    ;
    

到目前为止,一切看起来都不错。我可以登录用户(使用护照本地身份验证)并且响应是我希望的......

“用户”: "_id": "56c8b5bd80d16ef41ec705dd", “电子邮件”:“peachy@keen.com”, "密码": "$2a$10$zd ...等", “__v”:0, , "jwtToken": "JWT eyJ0eXAiOiJ .... 等"

我像这样创建了一个不受保护的测试路线......

// in my routes file
app.get('/user/tokenTest', user.tokenTest);

在我的控制器中,一个简单的端点...

exports.tokenTest = function(req, res) 
    console.log(req.headers);
    res.send("token test!!");
;

而且 GET 也可以正常工作。

    然后我尝试像这样保护那条路线:

    app.get('/user/tokenTest', passport.authenticate('jwt',  session: false ),
        user.tokenTest);
    

在我这样做之后,除了悲伤什么都没有。我发送这样的请求:

curl -k 'https://localhost:3443/user/tokenTest' -H 'Authorization: JWT eyJ0eXAiOiJ... etc.' 

总是,总是得到 401:

未经授权

控制器中的控制台日志似乎没有执行,passport.use 策略方法中的日志也没有。我已经调整和调整,但我有点迷失了。 passport-jwt 文档仅提供示例,几乎没有其他帮助。

请对我在上面犯的错误或至少如何进行调试有任何想法?

【问题讨论】:

你对app.get 路由做了什么?你说你保护了 app.post 路由,但是你的 curl 使用了 GET 方法。 @bolav - 不错。谢谢。我错误地将其保护为邮政路线。将其更改为获取(请参阅编辑),我仍然遇到基本相同的问题(请参阅更新的错误)。 @user1272965 我正在做一个类似的项目,你能告诉我你如何在客户端存储 jwt 吗?因为我们每次调用 API 时都需要在头部中使用它,所以你是如何将它存储在客户端的? 【参考方案1】:

我可能迟到了,但我遇到了类似的问题,我有另一个解决方案。您可以使用此options.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('JWT') 从身份验证标头中提取 JWT 令牌,格式如下:

授权:JWT JSON_WEB_TOKEN_STRING.....

这是我使用的文档:https://github.com/themikenicholson/passport-jwt

从请求中提取 JWT

可以通过多种方式将 JWT 包含在请求中。在 为了尽可能保持灵活性,JWT 是从 作为 jwtFromRequest 传入的用户提供的回调的请求 范围。这个回调,从现在开始被称为提取器, 接受请求对象作为参数并返回编码的 JWT 字符串或空值。包括提取器

提供了许多提取器工厂函数 护照-jwt.ExtractJwt。这些工厂函数返回一个新的 使用给定参数配置的提取器。

fromHeader(header_name) creates a new extractor that looks for the JWT in the given http header
fromBodyField(field_name) creates a new extractor that looks for the JWT in the given body field. You must have a body parser configured in order to use this method.
fromUrlQueryParameter(param_name) creates a new extractor that looks for the JWT in the given URL query parameter.
fromAuthHeaderWithScheme(auth_scheme) creates a new extractor that looks for the JWT in the authorization header, expecting the scheme to match auth_scheme.
fromAuthHeaderAsBearerToken() creates a new extractor that looks for the JWT in the authorization header with the scheme 'bearer'
fromExtractors([array of extractor functions]) creates a new extractor using an array of extractors provided. Each extractor is attempted in order until one returns a token.

【讨论】:

【参考方案2】:

对于跟随我的任何可怜的灵魂:passport-jwt 文档暗示 auth 标头应如下所示...

授权:JWT JSON_WEB_TOKEN_STRING.....

结果证明这是一种误导(无论如何,对我来说)。

幸运的是,感谢this article,我能够了解令牌是如何构建的。 (直到第一个 '.' 的令牌前缀是该方案的 base64 编码。前面的“JWT”是阻止验证工作的噪音。

所以解决方法是更改​​用户控制器返回的令牌:

    res.send( user: user, jwtToken: "JWT " + token );

为了更简单:

    res.send( user: user, jwtToken: token );

呸。是我,还是真的很糟糕,这么多节点包文档中对这些东西的解释不够充分??

【讨论】:

您是否尝试过通过fromHeader() 选项使用另一个标头参数?我无法让它以任何方式工作...... 这个为我节省了几个小时,+1 你救了我:)。用 Bearer TOKEN 击中我的 api 并击中我的头,为什么它不起作用。 这一定是某种版本控制问题,因为对我来说,如果没有 "JWT " 前缀,它就无法工作。如果有疑问,我想两者都试试。 我还需要 "JWT " 前缀 o_O 什么 F%&K 我讨厌处理节点文档

以上是关于使用 passport-jwt 验证节点 API的主要内容,如果未能解决你的问题,请参考以下文章

设置 passport-jwt 身份验证

使用 passport-jwt 进行 JWT 身份验证不考虑到期日期

使用passport-jwt在nestJs框架中进行角色验证

使用不同的用户角色在passport-jwt中设置身份验证很热门?

Apollo 服务器 - 仅使用 Passport-JWT 将身份验证应用于某些解析器

我可以有条件地使用passport-jwt吗?