JWT 概念介紹
JWT 是一種 Token,它的全名是 JSON Web Token。它由伺服器端產生後,交付給客戶端使用,Token 中會夾帶取多資訊,包含用戶的驗證資料或其他。
JWT 的表現形式是一個純粹的字串,這個字串有三個部分,分別為 Header、Payload、Signature,這三個部分會串接起來,用 .
來分隔,形成這樣的格式:Header}.{Payload}.{Signature}
,實際範例如下:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.B3GHLnjMFsZJc3K97UIWN68E8WovKxO0Qp6Ye4sVLzo
JWT 格式與驗證方式
JWT 透過驗證簽章 (Signature) 的方式,確保客戶端送過來的資訊是正確無誤的。
在談論簽章之前,我們先回來看一下 JWT 的格式,前面有提到 JWT 的 J 代表的是 JSON,可是這個東西看起來一點都不像我們熟悉的 JSON 格式?
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.B3GHLnjMFsZJc3K97UIWN68E8WovKxO0Qp6Ye4sVLzo
其實每個部分都有另外經過 Base64Url 編碼。
Header
經過 Base64Url 編碼前的 Header 其實是這個樣子:
{
"alg": "HS256",
"typ": "JWT"
}
這個形式就是我們熟悉的 JSON 了。alg
的部分表示簽章時所使用的演算法,這邊是 HS256,typ
則是指 Token 的類型,也就是 JWT 。
把上述格式的 JSON 去掉空白及換行符號,會得到:
{"alg":"HS256","typ":"JWT"}
將這串 JSON 進行 Base64Url 編碼,就會得到範例 Token 中的第一段。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload
跟前面一樣,Base64Url 編碼前的 Payload 是大家熟悉的 JSON 格式:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
在 payload 這邊可以用來放置使用者的資料,JWT 的術語稱為 claims,不確定怎麼翻譯比較準確,可以翻譯成宣稱或主張。應該是用於告訴使用不能只看這些 claims 就相信這些資料是正確的,必須透過簽章來檢驗正確性。
這邊的 sub
是 Subject 的縮寫,可以想像成使用者 id, iat
則是 Issued At 的縮寫,sub
及 iat
都算是 JWT 慣例的 claims,我們也可以放自己的 claims, 例如 name 就是使用者自己設定的 (詳情請看 JWT 官網及 RFC 文件)。
一樣,我們將 Palyload 去除掉空白及換行符號後得到:
{"sub":"1234567890","name":"John Doe","iat":1516239022}
將這串 JSON 進行 Base64Url 編碼,就會得到範例 TOKEN 中的第二段。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Signature
簽章就是 JWT 的核心所在。在前面的 Header 區塊中,有提到使用了使用了 HS256
作為簽章演算法,HS256
是 HMAC SHA256 的縮寫。
根據官方網站的說明,Signature 的規則是:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
也就是將 Base64Url 編碼後的 header 及 payload 串在一起後,再搭配 secret 來產生簽章。
這邊我使用的範例 secret 是 JWTDevinDeving
,所以實際上是這樣計算:
HMACSHA256(
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 + "." +
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ,
JWTDevinDeving
)
最後得到我們的 Signature,也就是範例 Token 的第三段:
B3GHLnjMFsZJc3K97UIWN68E8WovKxO0Qp6Ye4sVLzo
JWT 驗證原理
雜湊函式簡介
HMAC SHA256 是一種雜湊函式,簡單來說,就是一種會把字串轉換成亂碼的演算法。但特別的地方在於固定的字串會產生固定的亂碼,因此只要字串一改變,就會產生不一樣的亂碼。
我們用簡單的雜湊算法 md5 為例,
md5(apple) = 1f3870be274f6c49b3e31a0c6728957f
md5(appla) = df4c9efc0c4baefb6488cbef9678f11b
md5(banana) = 72b302bf297a228a75730123efef7c41
( md5 線上轉換工具 )
我們可以把 HMAC SHA256 想像成比較複雜精密的 md5,只要 header 跟 payload 有變化,就會產生出不一樣的字串。
但 HMAC 除了轉換的方式比較精密以外,它還需要配合 secret 使用。有了 secret,即便是一樣的原文,只要 secret 不一樣就會產生不一樣的結果。
例如,我將 JWTDevinDeving
換成 FalseJWTDevinDeving
後,則產生的 Signature 就會從原本的:
B3GHLnjMFsZJc3K97UIWN68E8WovKxO0Qp6Ye4sVLzo
變成:
Hr6Q7tsrzEu7OynS76nYJRUPphRfTKuBJDM748MGOso
JWT 如何利用雜湊函式來驗證
伺服器端在產生 Token 時,會使用自己的 secret 對 header 及 payload 進行簽章,產生 signature 後,製作成 JWT 後給客戶端使用。
客戶端下次提供 JWT 給伺服器端時,伺服器端會檢查客戶端所提供的 JWT 是否有效。
檢查的方式是,再產生一次 signature,並比對是否一致。
伺服器端會將客戶端提供的 JWT 的 header 及 payload 取出,再用 secret 來製作一次 signature,如果新製造出來的 signature 跟客戶端提供的 JWT 中的 signature 一致,伺服器端就會相信這個 JWT 是有效的。
這樣的流程是利用前面提到的 HMAC 的兩個特性,
- 只要內容有變動,signature 就一定會改變。
- 只要secret 不一樣,signature 就會不一樣。
所以只要 secret 不外流,惡意使用者即便串改內容,也沒辦法產生有效的 signature。
Cookie
Cookie 是什麼
Cookie 由伺服器端透過回應 (HTTP Response) 傳送給客戶端,並由客戶端儲存在瀏覽器中。Cookie 以一筆資料對應一個鍵值的方式儲存,也就是所謂的 key-value 格式。並且會包含一些額外資訊,例如失效時間、Domain 等。
在每次的請求 (Http Request) 中 ,瀏覽器會自動將相同 Domain 的 Cookie 夾帶在請求中送出。一個 Domain 可以有很多筆 Cookie,所以在請求中,所有相同 Domain 的 cookies 都會被瀏覽器一起帶過去給伺服器。
Cookie 可以被用來記錄使用者資訊,並透過這樣的方式來達到身分驗證、個人化、使用者行為追蹤等目的。
Cookie 如何驗證使用者身分
cookie 是客戶端的技術,結合伺服器端的技術 session,可以達到身分驗證的目的。
- 使用者透過瀏覽器送出了一筆請求給
https://test.com/hello
。
- 伺服器收到了訊息,並回應給瀏覽器,同時夾帶了 cookie
id=123
。 - 瀏覽器收到的回應,並將 cookie
id=123
儲存在瀏覽器中。
之後,
- 使用者送出了另一筆請求給
https://test.com/nice-to-meet-you
。這時因為瀏覽器中有儲存https://test.com
的 cookieid=123
,因此瀏覽器會自動將該 cookie 帶過去。 - 伺服器收到訊息,發現請求中有包含 cookie
id=123
,瀏覽器就可以知道這次的請求跟之前是同一個人。
在上面的例子中,其實伺服器在第一次傳送 cookie id=123
給客戶端時,也同時透過 session 儲存了 id=123
的資訊。
所以在之後從客戶端收到 id=123
的 cookie 時,伺服器就可以把 123
拿去與 session 中的資料做比對,確認是否存在。
這邊先簡單帶過 cookie 與 session,以後有機會在專文介紹。
JWT 跟 Cookie 比較
- JWT 是一種資料格式;Cookie 是一種儲存方式。
- Cookie 的 value 可以儲存任何字串,包含 JWT。
- JWT 如果不是存在 Cookie,通常必須要另外處理,才會帶給伺服器。
- 針對身分驗證用途,伺服器通常不儲存產生的 JWT,但使用 Cookie 時,通常會在伺服器儲存一份 Session 資料。