你真的深知JWT(JSON Web Token)了嗎?
頒發(fā)訪問令牌是授權服務的關鍵所在,OAuth2.0規(guī)并未約束訪問令牌內容的生成規(guī)則,只要符合唯一性、不連續(xù)性、不可猜性。
與其是一個隨機字符串,不如結構化令牌更有可讀性,用得最多的就是JWT。
什么是JWT?
JWT是一個開放標準(RFC 7519),它定義了一種緊湊自包含的方式,作為JSON對象安全傳輸信息,結構化封裝生成token的技術。
結構化后的token可被賦予豐富含義,這是與無意義的隨機字符串形式token的最大區(qū)別。
JWT 的結構是怎樣的?
HEADER(頭部)
裝載令牌類型和算法等信息,是JWT的頭部。
- typ 表示第二部分PAYLOAD是JWT類型
- alg 表示使用HS256對稱簽名的算法
PAYLOAD(數據體)
JWT的數據體,代表了一組數據。
- sub
令牌的主體,一般設為資源擁有者的唯一標識
- exp
令牌的過期時間戳
- iat
令牌頒發(fā)的時間戳
是JWT規(guī)范性的聲明,PAYLOAD表示的一組數據允許我們自定義聲明。
SIGNATURE(簽名)
簽名后的JWT整體結構,是被.分割的三段內容:header.payload.signature。JWT令牌直接用肉眼,看起來還是毫無意義,但如果拷貝到 https://jwt.io/ 在線校驗,即可看到解碼后的有意義數據。
SIGNATURE表示對JWT信息的簽名。
作用
可能你覺得,有了HEADER和PAYLOAD就可讓令牌攜帶信息在網絡中傳輸了,但在網絡中傳輸這樣的信息體不安全。必須加密簽名,而SIGNATURE就是對信息的簽名結果,當受保護資源接收到三方軟件的簽名后需要驗證令牌的簽名是否合法。
令牌內檢
定義
既然授權服務頒發(fā)令牌,受保護資源服務就要驗證令牌。而受保護資源調用授權服務提供的檢驗令牌的服務的這種校驗令牌方式就叫令牌內檢。
特點
有時授權服務依賴DB,然后受保護資源服務也依賴該DB,即“共享DB”。微服務架構下,不同系統(tǒng)間依靠服務而非DB通信,比如授權服務給受保護資源服務提供一個RPC服務:
JWT令牌本身包含了之前所要依賴DB或依賴RPC服務才能拿到的信息,比如某用戶為某軟件進行授權等信息。
JWT令牌怎么用?
- 有JWT令牌后的通信方式
授權服務發(fā)個令牌,受保護資源服務接這令牌,然后開始解析令牌所含信息,無需再去查詢DB或RPC調用。即實現了令牌內檢。
為什么令牌要編碼且簽名?
授權服務頒發(fā)JWT后給到xx軟件,xx拿著令牌請求受保護資源服務,即我在公眾號里的文章。顯然令牌要在公網傳輸。
所以傳輸過程令牌還要做到:
- 編碼,以防亂碼
- 簽名及加密,以防數據信息泄露。
JJWT是開源較方便的JWT工具,開箱即用。封裝Base64URL編碼和對稱HMAC、非對稱RSA的一系列簽名算法。
使用JJWT可方便生成一個經過簽名的JWT令牌,以及解析一個JWT令牌。
- String sharedTokenSecret="hellooauthhellooauthhellooauthhellooauth";//密鑰
- Key key = new SecretKeySpec(sharedTokenSecret.getBytes(),
- SignatureAlgorithm.HS256.getJcaName());
- //生成JWT令牌
- String jwts=
- Jwts.builder().setHeaderParams(headerMap).setClaims(payloadMap).signWith(key,SignatureAlgorithm.HS256).compact()
- //解析JWT令牌
- Jws<Claims> claimsJws =Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jwts);
- JwsHeader header = claimsJws.getHeader();
- Claims body = claimsJws.getBody();
JWT令牌的優(yōu)勢?
計算代替存儲
時間換空間思想。這種計算并結構化封裝,減少了“共享DB” 因遠程調用而帶來的網絡傳輸性能損耗,所以可能節(jié)省時間。
加密
因JWT令牌內部已包含重要信息,所以傳輸過程都必須被要求密文傳輸,被強制要求加密也保障了傳輸安全性。
增強系統(tǒng)可用性和可伸縮性
JWT令牌,通過“自編碼”方式包含身份驗證需信息,不再需要服務端額外存儲,所以每次的請求都是無狀態(tài)會話。符合盡可能遵循無狀態(tài)架構設計原則,即增強了系統(tǒng)可用性和伸縮性。
JWT令牌的缺陷
無法在使用過程中修改令牌狀態(tài)。
比如我在使用xx時,可能因為莫須有原因修改了在公眾號平臺的密碼或突然取消了給xx的授權。這時,令牌狀態(tài)就該有變更,將原來對應令牌置無效。
但使用JWT時,每次頒發(fā)的令牌都不會存在服務端,無法改變令牌狀態(tài)。這表示JWT令牌在有效期內暢通無阻。
那么可以把JWT令牌存儲在一個分布式內存數據庫比如Redis中嗎?
NO!這違背JWT意義 - 將信息結構化存入令牌本身。通常有兩種方案:
- 將每次生成JWT令牌時的秘鑰粒度縮小到用戶級別,即一個用戶一個秘鑰
如此,當用戶取消授權或修改密碼,可讓該密鑰一起修改。這種方案一般還需配套單獨密鑰管理服務
- 在不提供用戶主動取消授權的環(huán)境里面,若只考慮修改密碼場景,即可把用戶密碼作為JWT的密鑰。這也是用戶粒度。這樣用戶修改密碼也就相當于修改了密鑰。
令牌的生命周期
令牌都有有效期,只是JWT可把有效期的信息存在本身結構。
OAuth 2.0的令牌生命周期,通常有三種情況:
1.令牌自然過期
該過程不排除主動銷毀令牌的可能,比如令牌被泄露,授權服務可讓令牌失效。
2.訪問令牌失效后可使用刷新令牌請求新令牌,提高用戶使用三方軟件的體驗。
3.讓三方軟件比如xx,主動發(fā)起令牌失效請求,然后授權服務收到請求后讓令牌立即失效。
何時需要該機制?
比如用戶和三方軟件間存在一種訂購關系:我購買了xx軟件,那么到期或退訂時且我授權的token還未到期情況下,就需這樣一種令牌撤回協議,支持xx主動發(fā)起令牌失效請求。作為開放平臺,也建議有責任的三方軟件遵守這樣的一種令牌撤回協議。
總結
OAuth 2.0 的核心是授權服務,沒有令牌就沒有OAuth,令牌表示授權后的結果。令牌在OAuth 2.0系統(tǒng)中對于第三方軟件都是不透明的。需要關心令牌的,是授權服務和受保護資源服務。
- JWT 默認是不加密,但也是可以加密的。生成原始 Token 以后,可以用密鑰再加密一次
- JWT 不加密的情況下,不能將秘密數據寫入 JWT
- JWT 不僅可以用于認證,也可以用于交換信息。有效使用 JWT,可以降低服務器查詢數據庫的次數
- JWT 的最大缺點是,由于服務器不保存 session 狀態(tài),因此無法在使用過程中廢止某個 token,或者更改 token 的權限。也就是說,一旦 JWT 簽發(fā)了,在到期之前就會始終有效,除非服務器部署額外的邏輯
- JWT 本身包含了認證信息,一旦泄露,任何人都可以獲得該令牌的所有權限。為了減少盜用,JWT 的有效期應該設置得比較短。對于一些比較重要的權限,使用時應該再次對用戶進行認證
- 為了減少盜用,JWT 不應該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸
參考
JSON Web Token 入門教程
在OAuth 2.0中,如何使用JWT結構化令牌?
https://tools.ietf.org/html/rfc6749#section-4.4