原文鏈接:https://learnku.com/laravel/t/40490
討論請前往專業(yè)的 Laravel 開發(fā)者論壇:https://learnku.com/Laravel
你可以已經(jīng)聽說過 JSON Web Token (JWT) 是目前用于保護 API 的最新技術。
與大多數(shù)安全主題一樣,如果你打算使用它,那很有必要去了解它的工作原理(一定程度上)。問題在于,對 JWT 的大多數(shù)解釋都是技術性的,這一點讓人很頭疼。
讓我們看下,我能否解釋清楚 JWT 是如何在不引起你的注意下保護您的 API !
API 驗證
** 某些 API 資源需要限制訪問** 。例如,我們不希望一個用戶能夠更改另一個用戶的密碼。
這就是為什么我們保護某些資源,使用戶在允許訪問之前提供他的 ID 和密碼——換句話說,我們對它們進行身份驗證。
保護HTTP API的困難在于請求是 無狀態(tài)的 —— API 無法知道是否有兩個請求來自同一用戶。
那么,為什么不要求用戶在每次調(diào)用 API 時提供其 ID 和密碼呢?僅因為那將是可怕的用戶體驗。
JSON Web Token
我們需要的是一種允許用戶僅提供一次其憑證,隨后在后續(xù)請求中由服務器以另一種方式標識的方式。
為此設計了幾種系統(tǒng),當前的最新標準是 JSON Web Token。
這是一篇 關于該主題的精彩文章 ,它很好地比喻了 JSON Web Token 的工作方式:
想象一下你要入住酒店,而不是一個 API ?!窽oken」是塑料酒店安全卡,可用于進入你的房間和使用酒店設施,但不能進入任何其他人的房間。
當你退房的時候,你交回卡片。這類似于注銷。
Token 的結構
通常, JSON Web Token 是通過 HTTP 請求頭發(fā)送的。類似如下:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U
實際上, Token 部分是「Authorization: Bearer」之后的部分,僅是 HTTP 頭信息。
在你斷定這是難以理解的胡言亂語前,有幾件事你很容易注意到。
首先,Token是由三個不同的字符串組成,以句點分隔。這三個部分是 Base64 編碼 后的內(nèi)容,并且分別對應 Header , Payload 以及 Signature。
// Header eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
// Payload eyJzdWIiOiIxMjM0NTY3ODkwIn0
// Signature dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U
注意: Base64 是一種轉(zhuǎn)換字符串的方法,以確保在跨網(wǎng)絡傳輸期間不會被弄亂。這不是一種加密方式,任何人都可以 輕松解碼 以查看原始數(shù)據(jù)。
我們可以對這些字符串進行解碼,以更好地了解JWT的結構。
Header
以下是 Token 中的已解碼 Header 部分。Header 是 Token 的元信息。它并沒有告訴我們很多幫助你建立基本理解的知識,因此我們不會對此進行任何詳細介紹。
{
"alg": "HS256",
"typ": "JWT"
}
Payload
Payload 能引起更多的關注。如果你想, Payload 可以包含任何數(shù)據(jù),但是如果 Token 的目的是 API 訪問身份驗證,則可以僅包含用戶 ID 。
{
"userId": "1234567890"
}
請注意, Payload 不安全 。 任何人都可以解碼 Token ,并確切了解 Payload 中的內(nèi)容。因此,我們通常會包含一個 ID ,而不是諸如用戶電子郵件之類的敏感識別信息。
即使 Payload 是在 API 上識別用戶所需要的全部,它也不能提供身份驗證的方法。如果其中包含所有內(nèi)容,則有人可以輕松找到你的用戶 ID 并偽造 Token 。
因此,這使我們進入了 Signature 部分,這是認證 Token 的關鍵部分。
哈希算法
在解釋簽名如何工作之前,我們需要定義什么是哈希算法。
首先,它是一個將字符串轉(zhuǎn)換為稱為 Hash 的新字符串的函數(shù)。例如,假設我們要對字符串「Hello, world」進行哈希處理。這是我們使用 SHA256 哈希算法得到的輸出:
4ae7c3b6ac0beff671efa8cf57386151c06e58ca53a78d83f36107316cec125f
哈希的最重要屬性是 你無法通過哈希算法來查看 Hash 的原始文本 。
有許多不同類型的哈希算法,但 SHA256 通常與 JWT 一起使用。
換句話說,我們不能根據(jù)上面的散列值算出原始字符串是 Hello,world。哈希非常復雜,以至于無法猜測原始字符串。
JWT 簽名
回到 JWT 結構,來看一下令牌的第三部分,簽名。實際上需要計算:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
"secret string"
);
下面是對這里發(fā)生的情況做解釋:
首先, HMACSHA256 是哈希函數(shù)的名稱, 并帶有兩個參數(shù):要散列的字符串,以及「secret」。
其次,我們哈希的字符串是 base 64 的編碼報頭,加上 base 64 的編碼有效載荷。
第三, secret 是任意一段字符串,只有服務器知道。
問. 為什么在簽名散列中包含標頭和有效負載?
這確保了簽名對于此特定令牌是唯一的。*
問. secret 是什么?
為了回答這個問題,讓我們考慮一下如何偽造令牌。
我們之前說過,您無法通過查看輸出來確定哈希的輸入。但是,由于我們知道簽名包括標頭和有效負載,因為它們是公共信息,所以如果您知道哈希算法(提示:通常在標頭中指定),則可以生成相同的哈希。
但是只有服務器知道的秘密 * 不是 * 公共信息。將其包含在哈希中可防止某人生成自己的哈希來偽造令牌。而且由于散列會掩蓋用于創(chuàng)建散列的信息,因此任何人都無法從散列中找出秘密。
*將私有數(shù)據(jù)添加到哈希中的過程稱為 * salting ,幾乎不可能破解令牌。
認證過程
因此,現(xiàn)在您對令牌的創(chuàng)建方式有了一個很好的了解。您如何使用它來驗證您的API?
登錄
用戶登錄時會生成令牌,令牌會與用戶模型一起存儲在數(shù)據(jù)庫中。
- loginController.js *
if (passwordCorrect) {
user.token = generateToken(user.id);
user.save();
}
然后令牌作為authorization頭附加到登錄請求的響應中。
loginController.js
if (passwordCorrect) {
user.token = generateToken(user.id);
user.save();
res.headers("authorization", `Bearer ${token}`).send();
}
驗證請求
現(xiàn)在,客戶端有了令牌,他們可以將其附加到任何將來的請求以身份驗證用戶。
當服務器收到帶有授權令牌的請求時,將發(fā)生以下情況:
1.它解碼令牌并從有效載荷中提取ID。
2.它使用此ID在數(shù)據(jù)庫中查找用戶。
3.它將請求令牌與用戶模型中存儲的令牌進行比較。如果它們匹配,則對用戶進行身份驗證。
authMiddleware.js
const token = req.header.token;
const payload = decodeToken(token);
const user = User.findById(payload.id);
if (user.token = token) {
// 通過身份認證
} else {
// 未通過身份認證
}
退出登錄
如果用戶注銷,只需刪除附加到用戶模型的令牌,現(xiàn)在令牌將不再起作用。用戶將需要再次登錄以生成新令牌。
logoutController.js
user.token = null;
user.save();
總結
因此,這是關于如何使用 JSON Web 令牌保護 API 的最基本的說明。希望你不會很頭疼。
不過,相關的話題還有很多,所以這里有一些額外的讀物:
原文鏈接:https://learnku.com/laravel/t/40490
討論請前往專業(yè)的 Laravel 開發(fā)者論壇:https://learnku.com/Laravel