Node.js 使用 express-jwt 解析 JWT

Node.js 上 Token 鑑權經常使用的是 passport,它能夠自定義校驗策略,但若是你是用 express 框架,又只是解析 JWT 這種簡單需求,能夠嘗試下 express-jwt 這個中間件。git

關於 JWT

JWT 全稱 JSON Web Token,是代替傳統 session 認證的解決方案。其原理是服務端生成一個包含用戶惟一標識的 JSON 對象,頒發給客戶端。客戶端請求須要權限的接口時,只要把這個 JSON 再原樣發回給服務端,服務器經過解析就可識別用戶。github

它一般是這個樣子:web

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
複製代碼

這個 JSON 對象經過 . 分紅三段,包含了請求頭(加密算法)、負載信息(如 userId、過時時間),還有經過服務端密鑰生成的簽名來保證不被篡改。算法

這種機制使服務端再也不須要存儲 Token,所以是很是輕量的用戶認證方案。而且對於微服務這種須要不一樣服務間共用 Token 的跨域認證,JWT 是目前的首選。shell

關於 express-jwt

express-jwt 是 Node.js 的一個開源庫,由 ID 認證服務提供商 auth0 開發,是專用於 express 框架下解析 JWT 的中間件。express

它使用很是簡單,並且會自動把 JWT 的 payload 部分賦值於 req.user,方便邏輯部分調用。npm

開始使用

安裝

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 來訪問:json

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 插件 回覆【知乎】獲取最新知乎模擬登陸
相關文章
相關標籤/搜索