學習 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,我們可以使用任意類型的數據自定義它。有一些預留的鍵,包括 issexp,它們標識令牌的發行者和過期時間。

你可以通過使用一個對象將自己的數據添加到令牌中:

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 的資料。

你可能會花費很多時間閱讀部落格文章和意見。其中一些文章有: