JWT (JSON Web Token) - 原理介紹
自從 RESTful 架構興起後,越來越多人提倡使用 JWT 來取代傳統 Session 的場景,到底什麼是 JWT 呢,來看看吧~
JWT 介紹
- JWT 是 JSON Web Token 的簡寫,是一種開放標準 (RFC 7519),也就是基於 JSON object 的編碼,並透過這個編碼進行傳遞資訊。
- JWT 會透過 HMAC、RSA、ECDS 等演算法進行加密
- 通常利用 JWT 來對使用者進行驗證,也就是使用者會先請求身分提供的伺服器給予該 JWT,而後,只要使用者帶著這個 JWT 向資源伺服器請求資源,如果這個 JWT 是有效的,那麼就能獲取資源
JWT 的組成
JWT 的組合可以看成是三個 JSON object,並且用 **.** 來做區隔,而這三個部分會各自進行編碼,組成一個 JWT 字串。
也就是變成:xxxxx.yyyyy.zzzzz
-
Header
由兩個欄位組合:
-
alg
也就是 token 被加密的演算法,如 HMAC、SHA256、RSA
-
typ
也就是 token 的 type,基本上就是 JWT
範例:
1
2
3
4{
"alg": "HS256",
"typ": "JWT"
}然後進行 Base64 進行編碼。Base64 是透過 64 個字符來表示二進制數據的一種方法,編碼的方式是固定的而且是可以逆向解碼的,並不是那種安全的加密演算法。
-
-
Payload
這裡放的是聲明 (Claim) 內容,也就是用來放傳遞訊息的地方,在定義上有三種聲明:
-
Registered claims
可以想成是標準公認的一些訊息建議你可以放,但並不強迫,例如:
- iss (Issuer):JWT 簽發者
- exp (Expiration Time):JWT 的過期時間,過期時間必須大於簽發 JWT 時間
- sub (Subject):JWT 所面向的用戶
- aud (Audience):接收 JWT 的一方
- nbf (Not Before):也就是定義擬發放 JWT 之後,的某段時間點前該 JWT 仍舊是不可用的
- iat (Issued At):JWT 簽發時間
- jti (JWT Id):JWT 的身分標示,每個 JWT 的 Id 都應該是不重複的,避免重複發放
-
Public claims
這個,可以想成是傳遞的欄位必須是跟上面 Registered claims 欄位不能衝突,然後可以向官方申請定義公開聲明,會進行審核等步驟,實務上在開發上是不太會用這部分的。
-
Private claims
這個就是發放 JWT 伺服器可以自定義的欄位的部分,例如實務上會放 User Account、User Name、User Role 等不敏感的數據。
所謂不敏感的數據就是不會放使用者的密碼等敏感數據,因為該 Payload 傳遞的訊息最後也是透過 Base64 進行編碼,所以是可以被破解的,因此放使用者密碼會有安全性的問題。
範例:
1
2
3
4
5{
"sub": "1234567890",
"account": "[email protected]",
"role": "admin"
}個人覺得通常都會放 iat、exp 等標準欄位,因為通常會需要檢查 JWT 發送時間及是否過期,以及還有使用者帳號,為了方便查詢使用者的一些數據,通常以前的做法是 Session 裡面存放使用者帳號,現在改用 JWT 的 payload 上存放,以及角色身分的定義,可以用來看該使用者是否有權限取得後端 API 的內容。
-
-
Signature
由三大部分組成:
- base64UrlEncode(header)
- base64UrlEncode(payload)
- secret
也就是:
1
2
3
4HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)header 跟 payload 中間用 **.** 來串接,secret 是存放在伺服器端的秘密字串,最後將這三個部分串接再一起的字串進行加密演算法進行加密。
Note:
secret 是要保存在伺服器端的,這個 secret 一旦外洩給客戶端,客戶端就可以自己產生 JWT,並且透過該 JWT 存取資源,因此 secret 是永遠不該外流的。
最後將 Header、Payload、Signature 三者用 **.** 串聯在一起,就是一個合法簽發的 JWT 字串。
在 JWT 官網:https://jwt.io/
有提供現成的 JWT 簽發工具,可以使用該工具來看產生的 JWT 字串長怎樣:
我使用上面我寫的三個部份的內容各自填上去,左邊就會出現正確的 JWT 字串出來。紅色部分就是編碼後的 Header、紫色部分就是編碼後的 Payload,藍色部分就是將 Header、Payload、Secrect 合在一起並進行加密演算法加密後的編碼。最後就是用 **.** 來將三個部分串聯在一起。
客戶端如何用 JWT 來訪問資源?
-
前端會先透過存取後端的登入 API,後端驗證使用者帳密成功後,就會發放合法 JWT 字串
-
前端拿到 JWT 字串就會將 JWT 存放在 Local Storage 裡面
-
而後當前端要存取受保護的資源 API 時,只要在 Header 的填寫以下內容:
1
Authorization: Bearer <JWT token>
-
後端收到後,會去檢查 Authorization 的 JWT token 是否有效,如果有效,則允許前端訪問受保護的資源。在以前的 Session 的設計上,Session 會存放在 Redis 等這種快取資料庫,每當使用者訪問受保護的資源時,會先去存取資料庫的 Session 進行比對,有效則讓使用者存取,以 JWT 的方式可以降低查詢資料庫的需求。
JWT 的優缺點
優點:
- 採用 JSON object 的形式,大部分的程式語言皆支援
- 可存放一些使用者資訊,但並非是敏感的資訊
- 整個 JWT,只要 Payload 不要放過多的資訊,其實 Size 是相當小的
- 不用在 Server 的資料庫存放 Session,特別適合多台 Server 的情境下,使得擴展性容易,因為多台 Server 要使用 Session 的話,會有共享 Session 的問題產生,
- 對於現在手機上的 APP 的應用特別好,使用者不用每次打開 APP 都要重新輸入帳號與密碼
- 支持跨域請求,不會有傳統用 Cookie 進行跨域請求等問題
缺點:
-
JWT 沒辦法主動被中止,也就是說不能像 Session 一樣被強制無效,但是個人覺得這有很多方式可以避免
-
JWT 一旦洩漏會有很大的安全性的問題,但是洩漏通常會透過兩種方式:
-
駭客使用你的電腦,並得知 JWT
這… 你電腦都被攻陷了… 那就不好說了 XD
-
使用中間人攻擊的方式,擷取客戶端傳送伺服器端的封包,並獲取 JWT,但使用 HTTPS 傳輸可以大幅度降低該攻擊,只要定期更換 SSL 證書就可以了
-
總結
JWT 之所以會興起,除了因為 RESTful 架構出現,加上現在微服務的架構的關係,一般來說上線的系統,不太可能用單台伺服器來處理一切,多台伺服器處理 Session 會有其麻煩性,雖然可以用統一的資料庫進行存放 Session 來控制,但是會有效能的問題。
不過其實這還有很多探討的空間,在這篇文章沒有去說,Session/Cookie 的架構其實也是可以跟 JWT 並行,端看應用場景。但就我兩者都使用過的經驗來看,我覺得 JWT 的方式的確很簡單易懂,Session 的機制有時候的確很煩人。
下次帶來有關 JWT 的一些語言相關 Library 的使用方式~以及在 RESTful 場景下使用方式。
最後最後!請聽我一言!
如果你還沒有註冊 Like Coin,你可以在文章最下方看到 Like 的按鈕,點下去後即可申請帳號,透過申請帳號後可以幫我的文章按下 Like,而 Like 最多可以點五次,而你不用付出任何一塊錢,就能給我寫這篇文章的最大的回饋!