什么是JWT?
JSON Web Token(JWT)是一個開放標準(RFC 7519),它定義了一種緊湊的、自包含的方式,用于作為 JSON 對象在各方之間安全地傳輸信息。
說了一長串,不知道說的啥。。。。其實JWT就是用一種結(jié)構(gòu)化封裝的方式來生成token的技術(shù)。
JWT 這種結(jié)構(gòu)化體可以分為 HEADER(頭部)、PAYLOAD(數(shù)據(jù)體)和 SIGNATURE(簽名)三部分。經(jīng)過簽名之后的 JWT 的整體結(jié)構(gòu),是被句點符號分割的三段內(nèi)容,結(jié)構(gòu)為 header.payload.signature 。比如下面這個示例:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
我們拿著這個jwtToken去https://jwt.io/可以看到解密后的結(jié)果
HEADER:
{
"alg": "HS256",
"typ": "JWT"
}
PAYLOAD:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
- HEADER:表示裝載令牌類型和算法等信息,是 JWT 的頭部。其中,typ 表示第二部分 PAYLOAD 是 JWT 類型,alg 表示使用 HS256 對稱簽名的算法。
- PAYLOAD:數(shù)據(jù)體主要是我們的結(jié)構(gòu)化數(shù)據(jù),這組數(shù)據(jù)基本上都是我們自定義的信息。但是有幾個關(guān)鍵點大家需要注意一下:exp(令牌的過期時間戳)、iat(令牌頒發(fā)的時間戳)
- SIGNATURE:表示對 JWT 信息的簽名。其實就是對HEADER與PAYLOAD進行一次加密處理,主要是驗證整個JWT內(nèi)容有沒有被篡改。
JWT的優(yōu)點
其實JWT最大的優(yōu)點就是為了設(shè)計無狀態(tài)的服務(wù)。
我們經(jīng)常可以聽到Http協(xié)議是無狀態(tài)的,而session是有狀態(tài)的,但是到底什么是有狀態(tài),什么是無狀態(tài)呢?這里我給大家簡單的科普一下:
所謂“狀態(tài)”,就是為了保留程序的一些數(shù)據(jù)或是上下文。
所以,大家可以知道,我們的軟件本身,肯定是有狀態(tài)的?。ǜ杏X是廢話,如果軟件不保留程序的數(shù)據(jù),或者沒有上下文,那這個軟件有個毛用?。?/p>
但是我們各種架構(gòu)設(shè)計,程序設(shè)計,都在強調(diào)設(shè)計無狀態(tài)的服務(wù),這怎么做呢?其實說白了,就是讓我們寫的程序,盡可能的“無狀態(tài)”,而把“有狀態(tài)”放到Redis,MySQL或者其他的分布式文件系統(tǒng)中。那么我們的服務(wù)就能可以隨意地增加和減少節(jié)點,同樣可以隨意地搬遷。而且,無狀態(tài)的服務(wù)可以大幅度降低代碼的復雜度以及 Bug 數(shù),因為沒有狀態(tài),所以也沒有明顯的“副作用”。對開發(fā)和運維來說,無狀態(tài)服務(wù)比有狀態(tài)的服務(wù)方便的多。
所以,我們用JWT作為Token,主要是通過“自編碼”的方式包含了身份驗證需要的信息,不再需要服務(wù)端進行額外的存儲,所以每次的請求都是無狀態(tài)會話。這就符合了我們盡可能遵循無狀態(tài)架構(gòu)設(shè)計的原則,也就是增強了系統(tǒng)的可用性和伸縮性。
JWT的缺點
其實JWT的缺點,就是它的優(yōu)點。
因為Token如果用“自編碼”的方式,不去存儲,那么就有一個很大的問題:覆水難收。
- 用戶登出后,JWTToken依然有效
- 用戶改密碼后,舊的JWTToken依然有效
- 用戶被管理員踢出后,舊的JWTToken依然有效
- JWTToken沒辦法“續(xù)約”,只能用申請新的Token
如何解決JWT的缺點
想要解決JWT的缺點,只能把部分信息存儲在客戶端,這是個無解的問題,想要徹底的做成“自編碼”而且又解決缺點,這是不可能的。
-
以JWTToken為key,過期時間為value,在Redis等緩存中維護JWTToken的有效性
其實這個方法也是可以的,但是不太友好。因為JWT的意義就是是把全部的信息放到JWT的PAYLOAD里面,如果用JWTToken為key,部分信息為value放到Redis中,其實是違背了JWT的設(shè)計初衷。而且JWTToken非常長,也不適合做緩存的key。
-
以用戶ID為key,用戶密碼為value,或者為每個用戶生成一個秘鑰為value,放到Redis等緩存中
JWT的SIGNATURE部分,我們可以設(shè)置一個“秘鑰”,哪怕HEADER與PAYLOAD相同,而秘鑰不同,最終生成的SIGNATURE也是不同的。所以,我們可以為每個用戶生成一個專屬的秘鑰,放在Redis等緩存中,當用戶改了密碼,或者被管理員踢出后,我們可以換掉用戶的秘鑰。那么下次用戶請求服務(wù)器時,我們對JWTToken進行校驗的時候,秘鑰改了,生成的SIGNATURE肯定不同,這樣就可以“收回”已經(jīng)無效的JWTToken了。
使用JWT需要注意的點
使用JWT作為Token,一定要注意里面的信息是否能給到第三方。如果不得不包含一些不能給到第三方的信息,那么JWTToken必須要加密。
因為JWT本身是包含了結(jié)構(gòu)化信息的,這里面有用戶的信息。如果不加密的話,不管是第三方、或者是用戶通過瀏覽器,都能夠知道JWT里面隱藏的信息。特別是用于OAuth2協(xié)議時,如果直接把JWT作為訪問令牌,那么第三方就能知道JWT里面的信息,而且這里面的信息可能第三方根本就沒有獲取到權(quán)限。