學習 JWT 基礎知識及如何使用它
JSON Web Token 是一種用於創建應用程式的訪問令牌的標準。
它的運作方式是,伺服器生成一個令牌,用於驗證使用者身份,並將其發送給客戶端。
客戶端將在每個後續請求中將令牌發送回伺服器,因此伺服器知道該請求來自特定身份。
這種架構在現代 Web 應用程序中非常有效,在使用者驗證之後,我們會對 REST 或 GraphQL API 執行API請求。
誰在使用 JWT 呢?以 Google 為例,如果你使用 Google APIs,你會使用 JWT。
JWT 是被加密簽名的(但不會被加密,因此在將用戶數據存儲在 JWT 中時,使用 HTTPS 是必須的),所以當我們收到它時,我們可以確信它的可信度,沒有中間人可以截取並修改它,或者無效化它所保存的數據。
儘管如此,JWT 經常因過度使用而受到批評,尤其是在可以使用更少問題的解決方案時使用它們。
你需要對該主題形成自己的觀點。我並不主張某種技術優於其他,只是展示你在手邊擁有的所有機會和工具。
它們有什麼用途?主要用於 API 認證和服務器到服務器的授權。
JWT 令牌如何生成?
使用 Node.js 你可以通過以下代碼生成令牌的第一部分:
const header = { "alg": "HS256", "typ": "JWT" }
const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64')
我們設定 HMAC SHA256 作為簽名算法(JWT 支持多種算法),然後我們從這個 JSON 編碼對象創建一個緩存,並且使用 base64 編碼它。
部分的結果是 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
。
接下來我們添加 payload,我們可以使用任意類型的數據自定義它。有一些預留的鍵,包括 iss
和 exp
,它們標識令牌的發行者和過期時間。
你可以通過使用一個對象將自己的數據添加到令牌中:
const payload = { username: 'Flavio' }
我們將這個對象轉換為 JSON 編碼的 Buffer,並且像之前一樣編碼結果使用 base64:
const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64')
在這種情況下,部分的結果是 eyJ1c2VybmFtZSI6IkZsYXZpbyJ9
。
接下來,我們從標頭和有效載荷內容獲取簽名,這可以確保即使被攔截和修改,我們的內容也是不可更改的,因為我們的簽名將失效。為此,我們將使用 crypto
Node 模塊:
const crypto = require('crypto')
const jwtSecret = '秘密鑰匙'
const signature = crypto.createHmac('sha256', jwtSecret).update(encodedHeader + '.' + encodedPayload).digest('base64')
我們使用 秘密鑰匙
秘密鑰匙並創建基於加密簽名的 Base64 編碼表示。
我們的例子中簽名的值是
MQWECYWUT7bayj8miVgsj8KdYI3ZRVS+WRRZjfZrGrw=
我們差不多做完了,我們只需要通過用點分隔它們來連接標頭、有效載荷和簽名這三個部分:
const jwt = `${encodedHeader}.${encodedPayload}.${signature}`
API 認證
這可能是使用 JWT 的唯一明智的方式。
一種常見的情況是:您在服務註冊並從服務儀表板下載 JWT。從現在開始,您將使用它來對所有發送到伺服器的請求進行驗證。
另一種情況則相反,您在管理 API 時將 JWT 發送給客戶端,並希望您的用戶只需通過傳遞令牌即可發送後續請求。
在這種情況下,客戶端需要將令牌存儲在某個地方。最佳選擇是在 HttpOnly cookie 中。其他的方法都容易受到XSS攻擊的影響,因此應該避免使用。HttpOnly cookie 在 JavaScript 中是無法訪問的,並且在每個請求時自動發送到源伺服器,因此它非常適用於這種用例。
選擇最佳 JWT 模塊
根據您使用的程式語言和環境,您可以從許多模塊中進行選擇。最流行的模塊列在jwt.io網站上。
不要將 JWT 用作會話令牌
不應將 JWT 用於會話。使用常規的伺服器端會話機制,它更有效且不容易暴露數據。
資源
了解更多
互聯網上有很多關於 JWT 的資料。
你可能會花費很多時間閱讀部落格文章和意見。其中一些文章有: