JWT全名JSON Web Token,因符合RESTful API無狀態原則而誕生。
傳統我們通常會用cookie/session的方式來做身分認證,也就是user登入後,用session id當作一種token,來做身份認證。
然而用session這種方法server會有額外的儲存負擔,查詢也會需要時間,加上一般production server不會只有一台,如果用session的話會有server之間如何共享session的問題。(當然如果您的SLB是by ip分流那就另當別論了)
另外,使用JWT不會有像cookie一樣可能有無法跨域傳送的問題,也不會有CSRF攻擊的問題,因為我們不會把JWT儲存在cookie,不會讓瀏覽器自動送出,通常是駭客會透過攔截封包來擷取JWT,只不過還是建議使用HTTPS傳輸比較安全。
JWT字串由header, payload, signature三個部分組成,各自編碼後,用.串接起來即成為JWT字串。
JWT => Header.Payload.Signature
Header:
組成如下,alg是token被加密的演算法,typ是指token的type。
{
"alg": "HS256",
"typ": "JWT"
}
把這個json做base64 encode即為Header。
Payload:
這邊是放聲明訊息的地方,有以下幾種定義:
1. 標準聲明(Registered claims)
JWT規定的標準聲明,只是建議可以放,沒有強制一定要放。
2. 私有聲明(Private claims)
可自行定義的聲明,通常會放user的不敏感資料。
3. 公有聲明(Public claims)
實務上不太會需要用到,有時間再來研究。
Payload組成範例:
{
"iss": "http://www.example.com",
"exp": 1525318201,
"data": {
"user_id": 1,
"user_name": "Vic"
}
}
其中iss = JWT簽發者, exp = JWT的過期時間,這兩個都是標準聲明。標準聲明還有很多定義,可自行查詢。
data的部分就是自定義的user資料。
把這個json做base64 encode即為Payload。
Signature:
Signature是這樣組成的:
HMAC-SHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
其中secret=金鑰,只有伺服器知道這組金鑰。
HS256 = HMAC-SHA256
將header和payload json data做base64UrlEncode編碼後,用.來串接成data string,再使用header中指定的hash演算法HS256來對data string使用金鑰進行加密,產生簽名(signature)。
根據本文提到的範例,最終產生的JWT token如下圖:
Header.Payload.Signature
紅.紫.藍
JWT驗證流程如下:
1. 在前端打後端登入api,且user確認登入成功後,server會回傳這組JWT Token給前端。
2. 前端收到JWT Token後,一般會將其儲存在local storage中。
3. 之後前端要存取後端API的時候,需在header帶上這組token:
Authorization: Bearer ${JWT token}
假設JWT token是abc即為: Authorization: Bearer abc
4. 後端收到token後會驗證token有效性,決定是否可以存取此api。