一、什么是JWT
JWT(JSON Web Token) 是一個開放標準(RFC 7519),它定義了一種緊湊的、自包含的方式,用于作為JSON對象在各方之間安全地傳輸信息。該信息可以被驗證和信任,因為它是數字簽名的。
二、使用場景(來自理解JWT的使用場景和優劣)
- 一次性驗證:
比如用戶注冊后需要發一封郵件讓其激活賬戶,通常郵件中需要有一個鏈接,這個鏈接需要具備以下的特性:能夠標識用戶,該鏈接具有時效性(通常只允許幾小時之內激活),不能被篡改以激活其他可能的賬戶…這種場景就和 jwt 的特性非常貼近,jwt 的 payload 中固定的參數:iss 簽發者和 exp 過期時間正是為其做準備的。 - restful api的無狀態認證
使用 jwt 來做 restful api 的身份認證也是值得推崇的一種使用方案。客戶端和服務端共享 secret;過期時間由服務端校驗,客戶端定時刷新;簽名信息不可被修改。spring security oauth jwt 提供了一套完整的 jwt 認證體系。
三、JWT結構
JWT由三部分組成,它們之間用圓點(.)連接。這三部分分別是:
- Header
- Payload
- Signature
具體示例如下所示:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
3.1 Header
jwt的頭部由兩部分信息組成:
- type:聲明類型,這里是jwt
- alg:聲明加密的算法 通常直接使用 HMAC SHA256
完整的頭部信息如下:
{
"type":"jwt",
"alg":"HS256"
}
對頭部信息進行Base64編碼的得到第一部分的信息
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
3.2 Payload
載荷就是存放有效信息的地方,它包含聲明(要求)。聲明有三種類型:
- registered claims:標準中注冊的聲明。這里有一組預定義的聲明,它們不是強制的,但是推薦
- public claims:公共的聲明
- private claims:私有的聲明
標準中注冊的聲明 (建議但不強制使用) :
- iss: jwt簽發者
- sub: jwt所面向的用戶
- aud: 接收jwt的一方
- exp: jwt的過期時間,這個過期時間必須要大于簽發時間
- nbf: 定義在什么時間之前,該jwt都是不可用的
- iat: jwt的簽發時間
- jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊
公共的聲明 :
公共的聲明可以添加任何的信息,一般添加用戶的相關信息或其他業務需要的必要信息.但不建議添加敏感信息,因為該部分在客戶端可解密.
私有的聲明 :
私有聲明是提供者和消費者所共同定義的聲明,一般不建議存放敏感信息,因為base64是對稱解密的,意味著該部分信息可以歸類為明文信息。
對Payload進行Base64加密就得到了JWT第二部分的內容。
3.3 signature
JWT的第三部分是一個簽證信息,這個簽證信息由三部分組成:
- header (base64后的)
- payload (base64后的)
- secret
第三部分需要base64加密后的header和base64加密后的payload使用 .
連接組成的字符串,然后通過header中聲明的加密方式進行加鹽secret組合加密,然后就構成了JWT的第三部分。
注意:
secret是保存在服務器端的,JWT的簽發生成也是在服務器端的,secret就是用來進行JWT的簽發和JWT的驗證,所以,它就是你服務端的私鑰,在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味著客戶端是可以自我簽發JWT了。
四、JWT,OAuth2,Session對比
4.1 傳統的session認證
http協議本身是一種無狀態的協議,而這就意味著如果用戶向我們的應用提供了用戶名和密碼來進行用戶認證,那么下一次請求時,用戶還要再一次進行用戶認證才行,因為根據http協議,我們并不能知道是哪個用戶發出的請求,所以為了讓我們的應用能識別是哪個用戶發出的請求,我們只能在服務器存儲一份用戶登錄的信息,這份登錄信息會在響應時傳遞給瀏覽器,告訴其保存為cookie,以便下次請求時發送給我們的應用,這樣我們的應用就能識別請求來自哪個用戶了,這就是傳統的基于session認證。
但是這種基于session的認證使應用本身很難得到擴展,隨著不同客戶端用戶的增加,獨立的服務器已無法承載更多的用戶,而這時候基于session認證應用的問題就會暴露出來:
- Session: 每個用戶經過我們的應用認證之后,我們的應用都要在服務端做一次記錄,以方便用戶下次請求的鑒別,通常而言session都是保存在內存中,而隨著認證用戶的增多,服務端的開銷會明顯增大
- 擴展性: 用戶認證之后,服務端做認證記錄,如果認證的記錄被保存在內存中的話,這意味著用戶下次請求還必須要請求在這臺服務器上,這樣才能拿到授權的資源,這樣在分布式的應用上,相應的限制了負載均衡器的能力。這也意味著限制了應用的擴展能力
- CSRF: 因為是基于cookie來進行用戶識別的, cookie如果被截獲,用戶就會很容易受到跨站請求偽造的攻擊
4.2 基于token的鑒權機制
JWT和OAuth2都是基于token的鑒權機制。基于token的鑒權機制類似于http協議也是無狀態的,它不需要在服務端去保留用戶的認證信息或者會話信息。這就意味著基于token認證機制的應用不需要去考慮用戶在哪一臺服務器登錄了,這就為應用的擴展提供了便利。
其基本的流程如下:
- 用戶使用用戶名密碼來請求服務器
- 服務器進行驗證用戶的信息
- 服務器通過驗證發送給用戶一個token
- 客戶端存儲token,并在每次請求時附送上這個token值
- 服務端驗證token值,并返回數據
這個token必須要在每次請求時傳遞給服務端,它應該保存在請求頭里, 另外,服務端要支持CORS(跨來源資源共享)策略,一般我們在服務端這么做就可以了Access-Control-Allow-Origin: *
。
4.3 JWT 認證協議與 OAuth2.0 授權框架不恰當比較(來自基于 Token 的 JWT 認證協議與 OAuth2.0 授權框架不恰當比較)
之所以說是不恰當,是因為JWT和OAuth2是完全不通過的概念。既然 JWT 和 OAuth2 沒有可比性,為什么還要把這兩個放在一起說呢?很多情況下,在討論OAuth2的實現時,會把JSON Web Token作為一種認證機制使用。這也是為什么他們會經常一起出現。
- JWT 是一種認證協議
JWT提供了一種用于發布接入令牌(Access Token),并對發布的簽名接入令牌進行驗證的方法。 令牌(Token)本身包含了一系列聲明,應用程序可以根據這些聲明限制用戶對資源的訪問。 - OAuth2 是一種授權框架
OAuth2是一種授權框架,提供了一套詳細的授權機制。用戶或應用可以通過公開的或私有的設置,授權第三方應用訪問特定資源。 - JWT 使用場景
JWT 的主要優勢在于使用無狀態、可擴展的方式處理應用中的用戶會話。服務端可以通過內嵌的聲明信息,很容易地獲取用戶的會話信息,而不需要去訪問用戶或會話的數據庫。在一個分布式的面向服務的框架中,這一點非常有用。但是,如果系統中需要使用黑名單實現長期有效的 Token 刷新機制,這種無狀態的優勢就不明顯了。
-
優勢
- 快速開發
- 不需要 Cookie
- JSON 在移動端的廣泛應用
- 不依賴于社交登錄
- 相對簡單的概念理解
-
限制
- Token有長度限制
- Token不能撤銷
- 需要 Token 有失效時間限制(exp)
- OAuth2 使用場景
如果不介意API的使用依賴于外部的第三方認證提供者,你可以簡單地把認證工作留給認證服務商去做。也就是常見的,去認證服務商(比如 Facebook)那里注冊你的應用,然后設置需要訪問的用戶信息,比如電子郵箱、姓名等。當用戶訪問站點的注冊頁面時,會看到連接到第三方提供商的入口。用戶點擊以后被重定向到對應的認證服務商網站,獲得用戶的授權后就可以訪問到需要的信息,然后重定向回來。
-
優勢
- 快速開發
- 實施代碼量小
- 維護工作減少
- 可以和 JWT 同時使用
- 可針對不同應用擴展
-
限制
- 框架沉重