express編寫JWT中間件

最近由于項(xiàng)目需要,學(xué)習(xí)了一下JWT這種身份驗(yàn)證的方法。在這里做一下總結(jié)。
本文主要是做一些總結(jié),參考文章鏈接:JSON Web Token - 在Web應(yīng)用間安全地傳遞信息

什么是JWT

JWT是一種用于雙方之間傳遞安全信息的簡潔的、URL安全的表述性聲明規(guī)范。

  • 簡潔(Compact): 可以通過URL,POST參數(shù)或者在HTTP header發(fā)送,因?yàn)閿?shù)據(jù)量小,傳輸速度也很快;
  • 自包含(Self-contained):負(fù)載中包含了所有用戶所需要的信息,避免了多次查詢數(shù)據(jù)庫;

JWT結(jié)構(gòu)簡介

JWT結(jié)構(gòu):

  • Headers頭部
  • Payload負(fù)載
  • Signature簽名

整體結(jié)構(gòu)為:
xxxxx.yyyyy.zzzzz

Headers

headers中通常包含了兩部分: token類型和采用的加密算法

{
  "alg": "HS256",
  "typ": "JWT"
}
  • alg: 算法類型
  • typ: token類型

Payload

Token的第二部分時負(fù)載,它包含了claim,Claim是一些實(shí)體(通常指的用戶)的狀態(tài)和額外的元數(shù)據(jù),有三種類型的claim: reserved,publicprivate.

  • Reserved claims:這些claim是JWT預(yù)先定義的,在JWT中并不會強(qiáng)制使用它們,而是推薦使用,常用的有:
  • iss(簽發(fā)者)
  • exp(過期時間戳)
  • sub(面向的用戶)
  • aud(接收方)
  • iat(簽發(fā)時間)
  • Public claims: 根據(jù)需要定義自己的字段,注意應(yīng)該避免沖突
  • Private claims: 這些是自定義的字段,可以用來在雙方之間交換信息

舉個栗子:

{
  "sub": "4008517517",
  "name": "John Doe",
  "admin": true
}

Signature

創(chuàng)建簽名需要使用編碼后的headerspayload以及一個密鑰(secret),使用headers中制定簽名進(jìn)行簽名算法進(jìn)行簽名.例如如果希望使用HMAC SHA256算法,那么簽名應(yīng)該使用下列方式創(chuàng)建:

HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

簽名用于驗(yàn)證消息的發(fā)送者以及消息是沒有經(jīng)過篡改的.

完整的JWT

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0Nzk0NTYxMzI5NjJ9.yWGqnRYMhdLOEHlLI3_WIpHPtX3QHEdpJQHx_B0meaA

實(shí)現(xiàn)步驟

  • 客戶端發(fā)送用戶名與密碼到服務(wù)器
  • 服務(wù)器進(jìn)行信息核對
  • 服務(wù)器生成token(payload中包含的為user_id)
  • 服務(wù)器應(yīng)用返回token,以后每次用戶的請求都帶上token
  • 服務(wù)器應(yīng)用驗(yàn)證token有效性.例如:簽名是否正確?token是否過期?檢查token的接收方是否為自己(可選)?
  • 服務(wù)器應(yīng)用返回響應(yīng)的信息

node模塊jwt-simple

使用express框架,jwt-simple模塊來構(gòu)建中間件(另外,我在github上邊也找到express-jwt模塊,好像也還不錯的樣子,但是沒有實(shí)踐過)。

產(chǎn)生token

import * as express from 'express';
import * as bodyParser from 'body-parser';
import * as jwt from 'jwt-simple';

let app = express();

app.set('jwtTokenSecret', 'phpsucks!');
app.use(bodyParser.json());

app.post('/', (req, res) => {
  let user = req.body;
  // 產(chǎn)生token過期時間,這里設(shè)置
  let expires = Date.now() + 7*24*60*60*1000;
  let token = jwt.encode({
    iss: user.id, // issuer 表明請求的實(shí)體
    exp: expires, // expires token的生命周期
    aud: 'jser'
  }, app.get('jwtTokenSecret'));

  res.json({
    token: token,
    expires: expires
  });
});

獲取token并解析

app.get('/', (req, res) => {
  // 獲取token,這里默認(rèn)是放在headers的authorization
  let token = req.headers.authorization;
  if (token) {
    let decoded = jwt.decode(token, app.get('jwtTokenSecret'));
    // 判斷是否token已過期以及接收方是否為自己
    if(decoded.exp <= Date.now() || decoded.aud !== 'jser') {
      res.sendStatus(401)
    } else {
      res.sendStatus(200)
    }
  } else {
    res.sendStatus(401)
  }
});

相對于常用的session而言,JWT通過將狀態(tài)的記錄放置于客戶端,從而降低服務(wù)端因反復(fù)查詢數(shù)據(jù)庫而產(chǎn)生的壓力。這應(yīng)該就是JWT的最大優(yōu)點(diǎn)了吧!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容