使用 JWT 的承载方案的身份验证标头

Posted

技术标签:

【中文标题】使用 JWT 的承载方案的身份验证标头【英文标题】:Authentication header using bearer scheme for JWT 【发布时间】:2018-05-04 01:58:24 【问题描述】:

我正在使用 Express、Passport 和 jsonwebtoken 在 NodeJS 中为网站构建身份验证系统。 我到处搜索,但找不到问题的解决方案。

现在我有一个身份验证控制器:

module.exports = function()   
var strategy = new BearerStrategy(params, function(payload, done) 
    var decodedToken = jwtDecode(payload)
    db.default.Account.find(where: id: decodedToken.id).then(account =>
        if (account) 
            return done(null, 
                id: account.id,
                role: account.role
            )
         else 
            return done(new Error("User not found"), null)
        
    )
)

passport.use(strategy)
return 
    initialize: function() 
        return passport.initialize()
    ,
    authenticate: function() 
        return passport.authenticate("bearer", cfg.jwtSession)
    
   

我在其中使用 BearerStrategy 并且此代码有效,因为我的 /login 路由为用户创建了一个令牌并返回该令牌

  accountController.post("/login", function(req, res) 
     if (req.body.email && req.body.password) 
     var accEmail = req.body.email
     var accPassword = req.body.password
     db.default.Account.find(where: email: accEmail, password: 
       accPassword).then(account =>
     if (account) 
        var payload = 
           id: account.id,
           role: account.role
      
      var token = jwt.sign(payload, cfg.jwtSecret)
      console.log(token)
      res.status(200).json(
          token: 'Bearer ' + token
      )
        else 
         res.sendStatus(401)
     
   )
  else 
     res.sendStatus(401)
   
)

如果我使用 Postman 发送一个 HTTP 请求来尝试访问路由 /account 并且我将创建的令牌设置为标头,那么一切正常。

accountController.get('/account', auth.authenticate('bearer',  session: false ), function(req, res)
res.status(200).render('pages/homepage')
)

我无法回答的问题是: 用 res.json(token: token) 发回令牌是不够的,令牌需要存储在某个地方,对吧?我应该如何使用 RestAPI 存储令牌,此外,我应该如何在每个请求的 HTTP 标头中从客户端发送令牌?

我愿意接受有关如何在存储和发送 JWT 之间建立这种连接的建议(因为 JWT 的生成和验证工作) 谢谢

【问题讨论】:

【参考方案1】:

是的,这样发送令牌就足够了。您只需要确保客户端以某种方式收到令牌。在您的特定情况下,客户端将向 /login 端点发送 POST 请求,并从对请求的响应中读取令牌。在半伪代码中(客户端 javascript):

http.post('/login', (response) => 
  let token = response.split(' ')[1];
);

客户端存储令牌。它存储在localStorage、sessionStorage 或cookie 中。各种存储位置都有pros and cons。必须对客户端进行编程以将其存储在这些地方之一,而服务器根本不存储它。如果您使用的是 Angular 或 React 等前端框架,您可以使用 Google 和搜索词(例如“angular、jwt、 storage”)找到大量信息和示例。如果您使用的是 vanilla JS,您可以按照提供的链接中的示例进行操作。基于上面的半伪代码构建:

http.post('/login', (response) => 
  let token = response.split(' ')[1];
  localStorage.setItem('token', token);
);

令牌在名为授权的 HTTP 标头中与每个请求一起发送。标头应具有以下格式

Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

眼睛...东西是实际的标记。在您的情况下,您可能必须使用 bearer(非大写 b),因为您已将 Passport 设置为这样。您也可以在代码中将其设为 auth.authenticate('Bearer', ... 以使其更“正确”。在较旧的文档和博客文章中,您经常可以看到 JWT 而不是 Bearer,但这并不真正遵循事实上的标准。

如果您以这种方式发送,Passport 应该可以正确读取令牌。

如何在客户端添加Authorization头取决于你是否使用框架,以及客户端是否是浏览器。如果您使用的是香草 JS,您可以阅读 this 链接。如果您使用的是框架,您可以谷歌“设置授权标头,角度”或类似的东西。官方文档和博客文章中通常都有大量信息。如果您使用的是前端框架,则添加 Authorization 字段的代码部分通常称为 auth 拦截器。拦截器有点像中间件。

最后,你正在写这个:

我应该如何使用 RestAPI 存储令牌...

REST 并不真正关心令牌的存储方式。它在客户端完成,与 REST 无关。但是,REST 关心端点的名称。如果/login 是动词,它真的是正确的名称吗?问题被提出并回答here。

【讨论】:

以上是关于使用 JWT 的承载方案的身份验证标头的主要内容,如果未能解决你的问题,请参考以下文章

.Net SignalR 在还配置了 Cookie 身份验证时使用 JWT 承载身份验证

在 ASP.Net 应用程序上结合 ADFS 身份验证和 JWT 承载

用于授权和身份验证的多个 JWT 承载

Auth0 - 在 Owin 上使用带有承载访问令牌的 JWT 使用 RS256 进行身份验证

HTML Post 表单的身份验证标头

如何在 api 请求标头中插入 api 身份验证所需的 jwt 令牌?