哈嘍大家好,我是鹹魚。 最近寫的一個 Python 項目用到了 jwcrypto 這個庫,這個庫是專門用來處理 JWT 的,JWT 全稱是 JSON Web Token ,JSON 格式的 Token。 今天就來簡單入門一下 JWT。 官方介紹:https://jwt.io/introduction ...
哈嘍大家好,我是鹹魚。
最近寫的一個 Python 項目用到了 jwcrypto 這個庫,這個庫是專門用來處理 JWT 的,JWT 全稱是 JSON Web Token
,JSON 格式的 Token。
今天就來簡單入門一下 JWT。
官方介紹:https://jwt.io/introduction
先聊聊 Token
Token 的意思是令牌,通常用於身份驗證。
例如,當客戶端首次登錄伺服器時,伺服器會生成一個 Token 並返回給客戶端。此後,客戶端只需在請求數據時帶上這個 Token,無需再次提供用戶名和密碼。
為什麼要有 Token ?
我們知道 HTTP 請求是無狀態的,也就是說伺服器無法識別每個請求的發起者(例如,我登錄淘寶網後刷新頁面,會要求我重新輸入用戶名和密碼進行登錄)。
為瞭解決這個問題,出現了一種解決方案:即伺服器為每個客戶端分配一個 session。當客戶端發起請求時帶上這個 session,伺服器就能識別請求的發起者。
然而,這種方法很快暴露了一些弊端。首先是開銷問題。伺服器需要保存所有客戶端的 session,如果訪問的客戶端數量增加,伺服器將需要保存成千上萬個 session,帶來巨大的存儲開銷。
其次是跨域問題。例如,在集群架構中有兩台伺服器 A 和 B。如果鹹魚第一次請求到了 A 伺服器,A 伺服器保存了鹹魚的 session,但如果下一次請求被分配到 B 伺服器,B 伺服器上沒有鹹魚的 session,該怎麼辦?
一種解決方案是使用 session 粘滯(session sticky)方法,使鹹魚的每次請求都打到同一個伺服器(如 A 伺服器)。但如果 A 伺服器宕機了,請求就不得不轉到 B 伺服器,依然無法解決問題。
於是,有人提出了另一種思路:為什麼要讓伺服器保存 session 呢?可以讓每個客戶端自己保存!伺服器只負責生成 session,不負責保存。
但是,如果不保存 session,如何區分客戶端?又如何驗證 session 是伺服器生成的呢?
這時,人們想到了讓伺服器生成一個 token。這個 token 是通過伺服器獨有的密鑰和演算法(例如 RS256 演算法)生成的,並且伺服器不會保存這個 token。
這樣,客戶端發起請求時帶上這個 token,伺服器收到後會用相同的演算法和密鑰進行驗證。如果 token 匹配,伺服器就能驗證客戶端的身份。
再聊聊 JWT
簡單介紹了 token,我們來看看 JWT。
JWT(JSON Web Token)是一種在網路中以 JSON 格式安全地傳輸信息的令牌。其原理是:伺服器認證之後,生成一個 JSON 格式的 token,並將其發回給用戶,類似下麵這樣:
{
"alg": "RSA",
"name": "鹹魚",
"role": "管理員",
"exp": "2024-05-20T00:00:00Z"
}
之後客戶端與伺服器通信的時候都通過這個 JSON Token 來驗證身份,同時為了防止用戶篡改數據,伺服器在生成這個對象的時候,會加上簽名。
優點:
- 緊湊性:JWT 被設計成體積較小,便於 URL、POST 參數或 HTTP 頭部傳輸。
- 自包含:JWT 負載中包含所有必要的信息,不需要在每次請求時訪問資料庫。
- 安全性:JWT 可以簽名(使用 HMAC 或 RSA 演算法),確保數據的完整性和真實性。通過加密(例如 JWE)還可以保證數據的機密性。
結構組成:
JWT 由 Header、Payload、Signature(簽名) 三部分組成,其中 Header 和 Payload 都是 JSON 格式( JWT 中的 J ):
- Header : 描述 JWT 的元數據,定義了生成簽名的演算法以及
Token
的類型。 - Payload : 用來存放實際需要傳遞的數據
- Signature(簽名):伺服器通過 Payload、Header 和一個密鑰 (Secret) 使用 Header 裡面指定的簽名演算法(預設是 HMAC SHA256)生成。
編碼之後的 JWT 形式是一個很長的字元串,中間用點(.
)分隔成三個部分,寫成一行(裡面沒有換行),類似下麵這樣:
Header.Payload.Signature
關於 JWT 三部分的內容就不多講了,官網有詳細介紹。
jwcrypto 使用
我們可以使用 Python 的 jwcrypto
庫來生成和驗證 JWT、加密和解密數據,以及簽名和驗證簽名等操作。
jwcrypto
提供了一系列功能,包括但不限於:
- 生成和驗證 JWT:可以使用
jwcrypto
生成 JWT,併在接收端驗證 JWT 的有效性。 - 加密和解密數據:支持使用不同的演算法對數據進行加密和解密,例如 AES、RSA 等。
- 簽名和驗證簽名:支持使用不同的演算法對數據進行簽名,併在接收端驗證簽名的有效性。
- 密鑰管理:支持生成和管理密鑰對、公鑰和私鑰,以及密鑰的導入和導出。
安裝 jwcrypto
:
pip install jwcrypto
生成一個帶有簽名的 JWT,其中包含了指定的用戶 ID:
from jwcrypto import jwt,jwk
# 使用 RSA 演算法生成一個 2048 位的密鑰對。
key = jwk.JWK.generate(kty='RSA', size=2048)
payload = {'user_id': 123}
# 創建一個 JWT 對象,並指定其頭部(header)為使用 RS256 演算法進行簽名。
token = jwt.JWT(header={'alg': 'RS256'}, claims=payload)
# 使用之前生成的密鑰對 JWT 進行簽名。
token.make_signed_token(key)
生成 RSA 密鑰對並導出公私鑰:
from jwcrypto import jwk
# 生成 RSA 密鑰對
key = jwk.JWK.generate(kty='RSA', size=2048)
# 導出公鑰和私鑰
public_key = key.export_public()
private_key = key.export_private()
# 列印公鑰和私鑰
print("公鑰:")
print(public_key)
print("\n私鑰:")
print(private_key)