zoukankan      html  css  js  c++  java
  • Node.js 使用 express-jwt 解析 JWT

    Node.js 上 Token 鉴权常用的是 passport,它可以自定义校验策略,但如果你是用 express 框架,又只是解析 JWT 这种简单需求,可以尝试下 express-jwt 这个中间件。

    关于 JWT

    JWT 全称 JSON Web Token,是代替传统 session 认证的解决方案。其原理是服务端生成一个包含用户唯一标识的 JSON 对象,颁发给客户端。客户端请求需要权限的接口时,只要把这个 JSON 再原样发回给服务端,服务器通过解析就可识别用户。

    它通常是这个样子:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
    

    这个 JSON 对象通过 . 分成三段,包含了请求头(加密算法)、负载信息(如 userId、过期时间),还有通过服务端密钥生成的签名来保证不被篡改。

    这种机制使服务端不再需要存储 Token,因此是非常轻量的用户认证方案。并且对于微服务这种需要不同服务间共用 Token 的跨域认证,JWT 是目前的首选。

    关于 express-jwt

    express-jwt 是 Node.js 的一个开源库,由 ID 认证服务提供商 auth0 开发,是专用于 express 框架下解析 JWT 的中间件。

    它使用非常简单,而且会自动把 JWT 的 payload 部分赋值于 req.user,方便逻辑部分调用。

    开始使用

    安装

    npm install express-jwt
    

    加入中间件

    const expressJwt = require('express-jwt')
    
    app.use(expressJwt({
      secret: 'secret12345'  // 签名的密钥 或 PublicKey
    }).unless({
      path: ['/login', '/signup']  // 指定路径不经过 Token 解析
    }))
    

    生成 Token

    生成 Token 的方式依然使用 jsonwebtoken,比如将下列代码加入到登录接口的返回部分:

    const jwt = require('jsonwebtoken')
    
    app.post('/login', function (req, res) {
      // 注意默认情况 Token 必须以 Bearer+空格 开头
      const token = 'Bearer ' + jwt.sign(
        {
          _id: user._id,
          admin: user.role === 'admin'
        },
        'secret12345',
        {
          expiresIn: 3600 * 24 * 3
        }
      )
      res.json({
        status: 'ok',
        data: { token: token }
      })
    })
    

    获取解析内容

    当收到带 Token 的请求,如果解析成功,就可以在路由回调里通过 req.user 来访问:

    app.get('/protected', function (req, res) {
      if (!req.user.admin)
        return res.sendStatus(401)
      res.sendStatus(200)
    })
    

    req.user 实际就是 JWT 的 payload 部分:

    {
      _id: '5dbbc7daaf7dfe003680ba39',
      admin: true,
      iat: 1572587484,
      exp: 1573192284
    }
    

    解析失败

    如果解析失败,会抛出 UnauthorizedError,可以通过后置中间件来捕获:

    app.use(function (err, req, res, next) {
      if (err.name === 'UnauthorizedError') {   
        res.status(401).send('invalid token')
      }
    })
    

    修改结果字段

    默认解析结果会赋值在 req.user,也可以通过 requestProperty 来修改:

    app.use(expressJwt({
      secret: 'secret12345',
      requestProperty: 'auth'
    }))
    

    允许无 Token 请求

    当接口允许不带 Token 和带 Token 两种状态的访问时(比如文章详情登录后判断点赞),可以通过 credentialsRequired: false 来对无 Token 请求不进行解析和抛出异常。

    app.use(expressJwt({
      secret: 'secret12345',
      credentialsRequired: false
    }))
    

    自定义解析

    默认情况下,express-jwt 是从请求 Headers 的 Authorization 字段来获取 Token 并解析。

    通过 getToken 也可以自定义一些解析逻辑,比如使用其他 Header 字段,自定义抛出异常等:

    app.use(expressJwt({
      secret: 'secret12345',
      credentialsRequired: false,
      getToken: function fromHeaderOrQuerystring (req) {
        if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
          return req.headers.authorization.split(' ')[1]
        } else if (req.query && req.query.token) {
          return req.query.token
        }
        return null
      }
    }))
    

    作废 Token

    在 JWT 机制中,由于 Token 通常不进行存储,如果想作废某一条 Token,一般都是通过被动的方式。

    常用的方式是建立某个字段的黑名单(比如 TokenId),对所有 Token 进行过滤,express-jwt 专门提供了回调来处理这种情况:

    const expressJwt = require('express-jwt')
    const blacklist = require('./blacklist')
    
    let isRevokedCallback = function(req, payload, done){
      let issuer = payload.iss
      let tokenId = payload.jti
    
      blacklist.getRevokedToken(issuer, tokenId, function(err, token){
        if (err) { return done(err) }
        return done(null, !!token)  // 第二个参数为 true 则不通过
      })
    }
    
    app.use(expressJwt({
      secret: 'secret12345',
      isRevoked: isRevokedCallback
    }))
    

    更多用法可以查看 官方文档


    本文属于原创,首发于微信公众号「面向人生编程」,如需转载请后台留言。

    关注后回复以下信息获取更多资源
    回复【资料】获取 Python / Java 等学习资源
    回复【插件】获取爬虫常用的 Chrome 插件
    回复【知乎】获取最新知乎模拟登录

  • 相关阅读:
    Codeforces 798C. Mike and gcd problem 模拟构造 数组gcd大于1
    Codeforces 796C. Bank Hacking
    Codeforces 792B. Counting-out Rhyme
    gym 101164 H.Pub crawl 凸包
    hdu 6053 TrickGCD 筛法
    hdu 6041 I Curse Myself 无向图找环+优先队列
    bzoj 2243: [SDOI2011]染色 线段树区间合并+树链剖分
    codeforces gym 101164 K Cutting 字符串hash
    树链剖分求lca
    UESTC 1697 简单GCD问题(一) 筛法
  • 原文地址:https://www.cnblogs.com/zkqiang/p/11810203.html
Copyright © 2011-2022 走看看