JWT 介紹 - Step by Step

翻譯自 Mohamad Lawand 2021 年 3 月 11 日的文章 《Intro to JWT - Step by Step》 [1]

在本文中,我將向您介紹 JWT[2]。

我們今天要講的內容包含:

JWT 是什麼

JWT (JSON Web Token) 是一個開放標準 [3],它定義了一種以緊湊和自包含的方法,用於在雙方之間安全地傳輸編碼爲 JSON 對象的信息。

因此,簡單來說,它是 JSON 格式的加密字符串,其中包含敏感信息,它使我們能夠驗證不同服務間的發送者。

應該在什麼時候使用 JWT?

JWT 與 Session Id 比較

§ 小型 Web 應用程序

Session Id 實現

在傳統的 Web 應用程序中,我們使用 Session 來授權用戶,當用戶登錄到應用程序後,我們會爲該用戶分配一個唯一的 Session Id。我們將此 Session Id 保存在用戶瀏覽器的安全 cookie 中和服務器的內存中。我們對每個請求都使用相同的會話,以便服務器知道該用戶已通過身份驗證。對於每個請求,cookie 中的 Session Id 都會與服務器內存中的 Session Id 作匹配,以驗證用戶是否被授權。

JWT 實現

在 JWT 實現中,我們使用 JWT 授權用戶,當用戶登錄到應用程序後,就會爲每個通過身份驗證的用戶生成一個唯一的 JWT。我們將該 token 保存在瀏覽器的 local storage 或者 cookie 中,而不會在服務器端保存任何內容。對於每個請求,該 token 都會被髮送到服務器進行解密和驗證,以覈實該用戶是否已授權,不管以何種方式篡改了 token 都會被拒絕。

這種實現對於小型站點來說很好,僅僅因爲我們不再存儲 Session Id,從而通過減少服務器的負載,我們已經從 JWT 中看到了一些好處。

§ 高級 Web 應用程序(多個服務器)

如果我們的應用程序越來越受歡迎,需要我們對其進行擴展,會發生什麼呢?

Session Id 實現

我們需要有一臺連接到負載均衡器的新服務器,以便基於流量和可用性在 Web 服務器之間導航流量。這種實現給我們帶來了一個新的問題,如下所示:

如果_用戶 1_ 登錄到了_服務器 1_,那麼_服務器 1_ 已經將 session 保存在其內存中,當_用戶 1_ 發出另一個請求並且負載均衡器將該請求重定向到了_服務器 2_,而_服務器 2_ 沒有保存該 session 信息,這時會發生什麼情況?

用戶將被認爲已退出應用程序並被要求再次登錄,這不是一個好的用戶體驗。通常,我們解決這個問題的方法是引入緩存:

現在,所有的 Session 也將同時保存在緩存中,因此任何一臺服務器都可以檢查該 Session 是否存在,並可以利用它來驗證用戶並授予他們對應用程序的訪問權限。

儘管緩存解決了我們的問題,但是在生產環境中,這種解決方案有着昂貴的成本:

JWT 實現

讓我們來看看如何通過 JWT 實現來處理相同的情況。

不同於在 Cookie 中使用 Session Id 與服務器內存中的 Session 作匹配;我們可以使用 JWT 來代替它。此時,當用戶登錄到我們的應用程序時,服務器將不會生成 Session Id 並將其保存在內存中,而是會創建一個 JWT token,並對其進行編碼和序列化,然後使用自己的加密機制對其進行簽名。通過這種方式,服務將知道一旦對它做了變更或篡改,便將其變爲無效。由於通過服務器的加密機制對其進行了簽名,所以這是可以被檢驗的。

使用 JWT 可以更容易地管理可伸縮性,因爲我們不需要服務器來處理任何會話檢查或緩存檢查。請求可以轉發到負載均衡器爲其分配的任一服務器,而無需擔心會話的可用性。萬一某臺服務器宕機,所有的 token 將仍然有效,因爲所有服務器上的加密機制是一樣的。

§JWT 和 Session Id 的區別總結

讓我們來快速總結一下 JWT 和 Session Id 的區別

JWT

Session Id

JWT 結構

JSON Web Token 由三部分組成,以點(.)分隔,分別是:

因此,JWT 通常如下所示:

xxxxxx.yyyyyyy.zzzzzzzz

這種分隔使從視覺上更容易看出 token 的不同部分。讓我們來分解一下它的不同的部分。

Header 通常由兩部分組成:

例如:

{
  "alg": "HS256",
  "typ": "JWT"
}

然後,將此 JSON 以 Base64Url 編碼,形成 JWT 的第一部分。

§Payload (Data)

token 的第二部分是有效負載,其中包含 Claims(聲明)。Claims 是有關實體(通常是用戶)和其他數據的聲明。有三種類型的 Claims:registered、public 和 private claims。

舉一個有效負載的例子:

{
  "sub": "221122112",
  "name": "Mohamd Lawand",
  "admin": true,
  "exp": 15323232,
  "iat": 14567766 //  token 的簽發時間
}

然後,對有效負載進行 Base64Url 編碼,形成 JSON Web Token 的第二部分。

除非將其加密,否則請不要將機密信息放入 JWT 的 Payload 或 Header 元素中。

簽名

簽名使我們能夠驗證 token 是否有效和沒被篡改。它的工作方式是獲取 token 的前兩部分,將 Header 和 Payload 分別編碼爲 Base64,然後將它們用 “.” 連接起來。這樣我們就擁有了與用戶共享的所有數據。

然後,獲取在第一部分(Header)中提供的算法並應用於上面的連接結果。如果前兩部分的哈希結果與 token 的第三部分匹配,則表示此 JWT 是有效的;如果不匹配,則表示此 token 被修改過,是無效的。

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

這種方案的唯一威脅是密鑰在服務器以外的任何地方都可用。但是,如果我們保護好密鑰的安全,就沒有什麼能損害這一過程。

簽名被用於驗證消息在傳輸過程中沒有被篡改,而且,當 token 是使用私鑰簽名時,它還可以驗證 JWT 發送方的真實身份。

它的工作原理與密碼哈希非常相似——我們將兩部分組合在一起,並且使用特定的算法進行單向哈希,然後我們比較哈希的結果看它們是否有效。

§ 簽名方式

現在,讓我們來看一下對 JWT 進行簽名的方式:

簽名的 token 可以驗證其中包含的 Claims 的完整性,而加密的 token 則可以向其他方隱藏這些 Claims。

作者 :Mohamad Lawand
譯者 :技術譯民

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/qAvRwYWvPYishGkCDgd7oQ