JWT的使用流程


JWT的實現原理

 

一篇文章告訴你JWT的實現原理
發布於 3 個月前 作者 axetroy 3097 次瀏覽 來自 分享

在使用 JWT 的時候,有沒有想過,為什么我們需要 JWT?以及它的工作原理是什么?

我們就來對比,傳統的 session 和 JWT 的區別

我們以一個用戶,獲取用戶資料的例子

傳統的 session 流程

  1. 瀏覽器發起請求登陸
  2. 服務端驗證身份,生成身份驗證信息,存儲在服務端,並且告訴瀏覽器寫入 Cookie
  3. 瀏覽器發起請求獲取用戶資料,此時 Cookie 內容也跟隨這發送到服務器
  4. 服務器發現 Cookie 中有身份信息,驗明正身
  5. 服務器返回該用戶的用戶資料

JWT 流程

  1. 瀏覽器發起請求登陸
  2. 服務端驗證身份,根據算法,將用戶標識符打包生成 token, 並且返回給瀏覽器
  3. 瀏覽器發起請求獲取用戶資料,把剛剛拿到的 token 一起發送給服務器
  4. 服務器發現數據中有 token,驗明正身
  5. 服務器返回該用戶的用戶資料

你發現了嗎?好些並沒有什么區別,除了 session 需要服務端存儲一份,而 JWT 不需要

但實際上區別大了去了

  1. session 存儲在服務端占用服務器資源,而 JWT 存儲在客戶端
  2. session 存儲在 Cookie 中,存在偽造跨站請求偽造攻擊的風險
  3. session 只存在一台服務器上,那么下次請求就必須請求這台服務器,不利於分布式應用
  4. 存儲在客戶端的 JWT 比存儲在服務端的 session 更具有擴展性

對比完了 session 和 JWT 的區別,下面我們來看看那它的實現原理

JWT 工作原理

jwt

首先 JWT 長這個樣

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpcCI6IjEyNy4wLjAuMSIsInV1aWQiOiJmZjEyMTJmNS1kOGQxLTQ0OTYtYmY0MS1kMmRkYTczZGUxOWEiLCJpYXQiOjE1Mjc1MjMwMTd9.1C01cpOf1N3M9YAYvQjfcLbDGHZ4iPVncCGIoG-lpO0jHOIA_ZHtSMDvK1nzArLpGK5syQSwExsZJz2FJsd2W2TUiHQYtzmQTU8OBXX6mfSZRlkts675W5_WhIiOEwz69GFSD0AKXZifCRgIpKLC0n273MRMr0wJnuBi9ScfJ7YjSiqCr7qyQ5iXeOdS3ObT3wdjjk-Wu9wbWM7R25TFb-7PEZY7r_e8jmczPCVcNbOYegedu73T4d30kRn2jKufTGntD5hR6T9AQsgAMwVR1edEFflWb772TmrHI7WZOAivsBCN9sr4YiyYMvE8lcz_mBsgsunugGiHA3DGxB2ORbjIC8NPm8FI25zGOh9JIM2r_jFFTIm9GiuKtC8Ck8N3-eWi9u1NgBxwLdgN5JyCORnIOlciQEsScg-3SdCTM5LH_j6CeqQNwJxT4-oENzqLSTDJbP-SOj9nnx8HnJ5wh3n64rAvtc89CeTk7PhWFjksHDifngN-cnaszl5lqoF1enz5i9FYYELSjM-G7jns2SyY1MQeLRjuEDriPZtFaGmTW-RLH3gJfQXtbdpEo0_nHBqXEohwoN_FLKo4BNrEwshpyA7vkBpCQC0QALKyC1_L1Q5qduR6dDcqRozAo2VqJXmAKN0rvhLuIEHZkicOZY0Ds4So4GCcleqvFcxm1HQ

眼睛看仔細一些,你會發現 JWT 里面有兩個.

數據格式是這樣的 header.payload.signature

我們逐個逐個部分去分析,這個部分到底是干嘛的,有什么用

Header

JWT 的 header 中承載了兩部分信息

{ "alg": "RS256", "typ": "JWT" }
  • alg: 聲明加密的算法
  • typ: 聲明類型

對這個頭部信息進行 base64,即可得到 header 部分

const headerBuff = Buffer.from( JSON.stringify({ alg: "RS256", typ: "JWT" }) ); const header = headerBuff.toString("base64"); console.log(header); // eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9

Payload

payload 是主體部分,意為載體,承載着有效的 JWT 數據包,它包含三個部分

  • 標准聲明
  • 公共聲明
  • 私有聲明

標准聲明的字段

interface Stantar { iss?: string; // JWT的簽發者 sub?: string; // JWT所面向的用戶 aud?: string; // 接收JWT的一方 exp?: number; // JWT的過期時間 nbf?: number; // 在xxx日期之間,該JWT都是可用的 iat?: number; // 該JWT簽發的時間 jti?: number; //JWT的唯一身份標識 }

標准中建議使用這些字段,但不強制。

公共聲明的字段

interface Public { [key: string]: any; }

公共聲明字段可以添加任意信息,但是因為可以被解密出來,所以不要存放敏感信息。

私有聲明的字段

interface Private { [key: string]: any; }

私有聲明是 JWT 提供者添加的字段,一樣可以被解密,所以也不能存放敏感信息。

上面的 JWT 的 payload 結構是這樣的

{ "ip": "127.0.0.1", "uuid": "ff1212f5-d8d1-4496-bf41-d2dda73de19a", "iat": 1527523017 }

同樣是通過 base64 加密生成第二部分的 payload

const payloadBuffer = Buffer.from( JSON.stringify({ ip: "127.0.0.1", uuid: "ff1212f5-d8d1-4496-bf41-d2dda73de19a", iat: 1527523017 }) ); const payload = payloadBuffer.toString("base64"); console.log(payload); // eyJpcCI6IjEyNy4wLjAuMSIsInV1aWQiOiJmZjEyMTJmNS1kOGQxLTQ0OTYtYmY0MS1kMmRkYTczZGUxOWEiLCJpYXQiOjE1Mjc1MjMwMTd9

Signature

signature 是簽證信息,該簽證信息是通過headerpayload,加上secret,通過算法加密生成。

公式 signature = 加密算法(header + "." + payload, 密鑰);

上面的 header 中,我們已經定義了加密算法使用 RS256,也已經實現了生成headerpayload,下面我們來生成 signature

const crypto = require("crypto"); const sign = crypto.createSign("SHA256"); const secret = `私鑰,太長我就不貼出來了`; sign.write(header + "." + payload); sign.end(); const signature = sign .sign(secret, "base64") // 在JWT庫中,已經把這些字符過濾掉了 .replace(/=/g, "") .replace(/\+/g, "-") .replace(/\//g, "_"); console.log(signature);

到此,已經實現了如何生成一個 JWT 的 token.

它是如何做身份驗證的?

首先,JWT 的 Token 相當是明文,是可以解密的,任何存在 payload 的東西,都沒有秘密可言,所以隱私數據不能簽發 token。

而服務端,拿到 token 后解密,即可知道用戶信息,例如本例中的uuid

有了 uuid,那么你就知道這個用戶是誰,是否有權限進行下一步的操作。

Token 的過期時間怎么確定?

payload 中有個標准字段 exp,明確表示了這個 token 的過期時間.

服務端可以拿這個時間與服務器時間作對比,過期則拒絕訪問。

如何防止 Token 被串改?

此時 signature字段就是關鍵了,能被解密出明文的,只有headerpayload

假如黑客/中間人串改了payload,那么服務器可以通過signature去驗證是否被篡改過。

在服務端在執行一次 signature = 加密算法(header + "." + payload, 密鑰);, 然后對比 signature 是否一致,如果一致則說明沒有被篡改。

所以為什么說服務器的密鑰不能被泄漏。

如果泄漏,將存在以下風險:

  • 客戶端可以自行簽發 token
  • 黑客/中間人可以肆意篡改 token

安全性相關

如果加強 JWT 的安全性?

根據我的使用,總結以下幾點:

  1. 縮短 token 有效時間
  2. 使用安全系數高的加密算法
  3. token 不要放在 Cookie 中,有 CSRF 風險
  4. 使用 HTTPS 加密協議
  5. 對標准字段 iss、sub、aud、nbf、exp 進行校驗
  6. 使用成熟的開源庫,不要手賤造輪子
  7. 特殊場景下可以把用戶的 UA、IP 放進 payload 進行校驗(不推薦)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM