1. 摘要
JSON Web Token(縮寫 JWT)是目前最流行的跨域認證解決方案购撼,本文介紹它的原理,用法和詳細的數(shù)據(jù)結(jié)構(gòu)匕累。
2. JWT的定義
Json web token(JWT)是為了網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開發(fā)標(biāo)準(RFC 7519)陵刹,該token被設(shè)計為緊湊且安全的,特別適用于分布式站點的單點登陸(SSO)場景欢嘿。JWT的聲明一般被用來在身份提供者和服務(wù)提供者間傳遞被認證的用戶身份信息衰琐,以便于從資源服務(wù)器獲取資源,也可以增加一些額外的其它業(yè)務(wù)邏輯所必須的聲明信息炼蹦,該token也可直接被用于認證羡宙,也可被加密。
什么情況下使用JWT比較適合掐隐?
授權(quán):這是最常見的使用場景狗热,解決單點登錄問題。因為JWT使用起來輕便虑省,開銷小匿刮,服務(wù)端不用記錄用戶狀態(tài)信息(無狀態(tài)),所以使用比較廣泛探颈;
信息交換:JWT是在各個服務(wù)之間安全傳輸信息的好方法熟丸。因為JWT可以簽名,例如膝擂,使用公鑰/私鑰對兒 - 可以確定請求方是合法的虑啤。此外隙弛,由于使用標(biāo)頭和有效負載計算簽名,還可以驗證內(nèi)容是否未被篡改狞山。
3. JWT的原理和流程
3.1 跨域認證的問題
互聯(lián)網(wǎng)服務(wù)離不開用戶認證全闷。一般流程是下面這樣:
1、用戶向服務(wù)器發(fā)送用戶名和密碼萍启。
2总珠、服務(wù)器驗證通過后,在當(dāng)前對話(session)里面保存相關(guān)數(shù)據(jù)勘纯,比如用戶角色局服、登錄時間等等。
3驳遵、服務(wù)器向用戶返回一個 session_id淫奔,寫入用戶的 Cookie。
4堤结、用戶隨后的每一次請求唆迁,都會通過 Cookie,將 session_id 傳回服務(wù)器竞穷。
5唐责、服務(wù)器收到 session_id,找到前期保存的數(shù)據(jù)瘾带,由此得知用戶的身份鼠哥。
這種模式的問題在于,擴展性(scaling)不好看政。單機當(dāng)然沒有問題朴恳,如果是服務(wù)器集群,或者是跨域的服務(wù)導(dǎo)向架構(gòu)允蚣,就要求 session 數(shù)據(jù)共享菜皂,每臺服務(wù)器都能夠讀取 session。
舉例來說厉萝,A 網(wǎng)站和 B 網(wǎng)站是同一家公司的關(guān)聯(lián)服務(wù)』衅現(xiàn)在要求,用戶只要在其中一個網(wǎng)站登錄谴垫,再訪問另一個網(wǎng)站就會自動登錄章母,請問怎么實現(xiàn)?
一種解決方案是 session 數(shù)據(jù)持久化翩剪,寫入數(shù)據(jù)庫或別的持久層乳怎。各種服務(wù)收到請求后,都向持久層請求數(shù)據(jù)前弯。這種方案的優(yōu)點是架構(gòu)清晰蚪缀,缺點是工程量比較大秫逝。另外,持久層萬一掛了询枚,就會單點失敗违帆。
另一種方案是服務(wù)器索性不保存 session 數(shù)據(jù)了,所有數(shù)據(jù)都保存在客戶端金蜀,每次請求都發(fā)回服務(wù)器刷后。JWT 就是這種方案的一個代表。
3.2 JWT 的原理
JWT 的原理是渊抄,服務(wù)器認證以后尝胆,生成一個 JSON 對象,發(fā)回給用戶护桦,就像下面這樣含衔。
{
"姓名": "張三",
"角色": "管理員",
"到期時間": "2018年7月1日0點0分"
}
以后,用戶與服務(wù)端通信的時候二庵,都要發(fā)回這個 JSON 對象抱慌。服務(wù)器完全只靠這個對象認定用戶身份。為了防止用戶篡改數(shù)據(jù)眨猎,服務(wù)器在生成這個對象的時候,會加上簽名强经。
服務(wù)器就不保存任何 session 數(shù)據(jù)了睡陪,也就是說,服務(wù)器變成無狀態(tài)了匿情,從而比較容易實現(xiàn)擴展兰迫。
區(qū)別
(1) session 存儲在服務(wù)端占用服務(wù)器資源,而 JWT 存儲在客戶端
(1) session 存儲在 Cookie 中炬称,存在偽造跨站請求偽造攻擊的風(fēng)險
(2) session 只存在一臺服務(wù)器上汁果,那么下次請求就必須請求這臺服務(wù)器,不利于分布式應(yīng)用
(3) 存儲在客戶端的 JWT 比存儲在服務(wù)端的 session 更具有擴展性
3.3 JWT的認證流程圖
流程說明:
1玲躯,瀏覽器發(fā)起請求登陸据德,攜帶用戶名和密碼;
2跷车,服務(wù)端驗證身份棘利,根據(jù)算法,將用戶標(biāo)識符打包生成 token,
3朽缴,服務(wù)器返回JWT信息給瀏覽器善玫,JWT不包含敏感信息;
4密强,瀏覽器發(fā)起請求獲取用戶資料茅郎,把剛剛拿到的 token一起發(fā)送給服務(wù)器蜗元;
5,服務(wù)器發(fā)現(xiàn)數(shù)據(jù)中有 token系冗,驗明正身奕扣;
6,服務(wù)器返回該用戶的用戶資料毕谴;
3.4 JWT的6個優(yōu)缺點
1成畦、JWT默認不加密,但可以加密涝开。生成原始令牌后循帐,可以使用改令牌再次對其進行加密。
2舀武、當(dāng)JWT未加密方法是拄养,一些私密數(shù)據(jù)無法通過JWT傳輸。
3银舱、JWT不僅可用于認證瘪匿,還可用于信息交換。善用JWT有助于減少服務(wù)器請求數(shù)據(jù)庫的次數(shù)寻馏。
4棋弥、JWT的最大缺點是服務(wù)器不保存會話狀態(tài),所以在使用期間不可能取消令牌或更改令牌的權(quán)限诚欠。也就是說顽染,一旦JWT簽發(fā),在有效期內(nèi)將會一直有效轰绵。
5粉寞、JWT本身包含認證信息,因此一旦信息泄露左腔,任何人都可以獲得令牌的所有權(quán)限唧垦。為了減少盜用,JWT的有效期不宜設(shè)置太長液样。對于某些重要操作振亮,用戶在使用時應(yīng)該每次都進行進行身份驗證。
6鞭莽、為了減少盜用和竊取双炕,JWT不建議使用HTTP協(xié)議來傳輸代碼,而是使用加密的HTTPS協(xié)議進行傳輸撮抓。
4. JWT 的數(shù)據(jù)結(jié)構(gòu)
4.1 JWT消息構(gòu)成
一個token分3部分妇斤,按順序:
- 頭部(header)
- 載荷(payload)
- 簽證(signature)
對象為一個很長的字符串,字符之間通過"."分隔符分為三個子串。注意JWT對象為一個長字串站超,各字串之間也沒有換行符荸恕,一般格式為:xxxxx.yyyyy.zzzzz 。
例如 yJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
4.2 頭部(header)
JWT的頭部承載兩部分信息:
- 聲明類型死相,這里是jwt
- 聲明加密的算法 通常直接使用 HMAC SHA256
JWT里驗證和簽名使用的算法融求,可選擇下面的:
JWS | 算法名稱 | 描述 |
---|---|---|
HS256 | HMAC256 | HMAC with SHA-256 |
HS384 | HMAC384 | HMAC with SHA-384 |
HS512 | HMAC512 | HMAC with SHA-512 |
RS256 | RSA256 | RSASSA-PKCS1-v1_5 with SHA-256 |
RS384 | RSA384 | RSASSA-PKCS1-v1_5 with SHA-384 |
RS512 | RSA512 | RSASSA-PKCS1-v1_5 with SHA-512 |
ES256 | ECDSA256 | ECDSA with curve P-256 and SHA-256 |
ES384 | ECDSA384 | ECDSA with curve P-384 and SHA-384 |
ES512 | ECDSA512 | ECDSA with curve P-521 and SHA-512 |
JWT的頭部描述JWT元數(shù)據(jù)的JSON對象參考:
{
"alg": "HS256",
"typ": "JWT"
}
代碼樣例如下:
// header Map
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
4.3 載荷(payload)
Payload 部分也是一個 JSON 對象,用來存放實際需要傳遞的數(shù)據(jù)算撮。JWT 規(guī)定了7個官方字段生宛,供選用。
iss (issuer):簽發(fā)人
exp (expiration time):過期時間
sub (subject):主題
aud (audience):受眾
nbf (Not Before):生效時間
iat (Issued At):簽發(fā)時間
jti (JWT ID):編號
除以上默認字段外肮柜,我們還可以自定義私有字段陷舅,如下例:
{
"sub": "1234567890",
"name": "chongchong",
"admin": true
}
注意,JWT 默認是不加密的审洞,任何人都可以讀到莱睁,所以不要把秘密信息放在這個部分。
這個 JSON 對象也要使用 Base64URL 算法轉(zhuǎn)成字符串芒澜。
代碼樣例如下:
JWT.create().withHeader(map) // header
.withClaim("iss", "Service") // payload
.withClaim("aud", "APP")
.withIssuedAt(iatDate) // sign time
.withExpiresAt(expiresDate) // expire time
.withClaim("name", "cy") // payload
.withClaim("user_id", "11222");
4.4 簽名(signature)
Signature 部分是對前兩部分的簽名仰剿,防止數(shù)據(jù)篡改。
首先痴晦,需要指定一個密鑰(secret)南吮。這個密鑰只有服務(wù)器才知道,不能泄露給用戶誊酌。然后部凑,使用 Header 里面指定的簽名算法(默認是 HMAC SHA256),按照下面的公式產(chǎn)生簽名术辐。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
算出簽名以后,把 Header施无、Payload辉词、Signature 三個部分拼成一個字符串,每個部分之間用"點"(.)分隔猾骡,就構(gòu)成整個JWT對象TOKEN瑞躺, 就可以返回給用戶。
4.4.1 Base64URL算法
前面提到兴想,Header 和 Payload 串型化的算法是 Base64URL幢哨。這個算法跟 Base64 算法基本類似,但有一些小的不同嫂便。
JWT 作為一個令牌(token)捞镰,有些場合可能會放到 URL(比如 api.example.com/?token=xxx)。Base64 有三個字符+、/和=岸售,在 URL 里面有特殊含義践樱,所以要被替換掉:=被省略、+替換成-凸丸,/替換成_ 拷邢。這就是 Base64URL 算法。
4.5 JWT的用法
客戶端接收服務(wù)器返回的JWT屎慢,將其存儲在Cookie或localStorage中瞭稼。
此后,客戶端將在與服務(wù)器交互中都會帶JWT腻惠。如果將它存儲在Cookie中环肘,就可以自動發(fā)送,但是不會跨域妖枚,因此一般是將它放入HTTP請求的Header Authorization字段中廷臼。
Authorization: Bearer <token>
當(dāng)跨域時,也可以將JWT被放置于POST請求的數(shù)據(jù)主體中绝页。
5. JWT荠商、JWS、JWE的區(qū)別
1)JWT(JSON Web Tokens)续誉,jwt長度較小莱没,且可以使用URL傳輸(URL safe)。不想cookies只能在web環(huán)境起作用酷鸦。 JWT可以同時使用在web環(huán)境和RESTfull的接口饰躲。
2)對于開發(fā)者來說,JWT與另外兩種相近的標(biāo)準:JWS(JSON Web Signature)臼隔、JWE(JSON Web Encryption)嘹裂,容易引起混亂。
3)關(guān)于JWT的描述摔握,可以參考RFC7519(https://tools.ietf.org/html/rfc7519)的描述:
**JSON Web Token (JWT) **是一個間接地寄狼、URL安全的,表現(xiàn)為一組聲明氨淌,可以在雙方之間進行傳輸泊愧。一個JWT的聲明,是指經(jīng)過編碼后的一個JSON對象盛正,這個JSON對象可以是一個JSON Web Signature(JWS)結(jié)構(gòu)的荷載(payload)删咱,或者是一個JSON Web Encryption(JWE)結(jié)構(gòu)的明文。允許使用聲明進行數(shù)字簽名豪筝,或者通過一個Message Authentication Code(MAC)進行完整性保護可選擇是否加密痰滋。
簡單來說摘能,JWTs表現(xiàn)為一組被編碼為JWS and/or JWE結(jié)構(gòu)的JSON object的聲明(Claim).
換言之,一組JWT聲明(就是表現(xiàn)為JSON格式的Claims)被通過JWS結(jié)構(gòu)或者JWE結(jié)構(gòu)(或者同時使用兩種)發(fā)送即寡,決定于你如何去實現(xiàn)它徊哑。所以,當(dāng)你發(fā)送JWT給別人是聪富,它實際上是一個JWT荷載或者JWE荷載莺丑。JWS荷載更加常用。
4)關(guān)于JWS
顧名思義墩蔓,JWS模式對這個內(nèi)容進行了數(shù)字化簽名梢莽。這個內(nèi)容被用來存放JWT的聲明.服務(wù)端簽名出JWT并且發(fā)送到客戶端,并在用戶成功認證后進行應(yīng)答奸披。服務(wù)器期望客戶端在下次請求的時候?qū)WS作為請求的一部分昏名,發(fā)送回服務(wù)端。
如果我們處理的客戶端是欺騙者怎么辦呢阵面?這就是簽名(signature)需要出場的地方了轻局。簽名攜帶了完整的可驗證的信息。換句話說样刷,服務(wù)器可以確認仑扑,接收到的JWT聲明里的JWS是沒有經(jīng)過欺騙客戶端、中間者進行修改的置鼻。
服務(wù)端通過驗證消息的簽名來確闭蛞客戶端沒有修改聲明。如果服務(wù)端檢測到任何修改箕母,可以采取適當(dāng)?shù)膭幼?拒絕這次請求或者鎖定客戶端之類的)储藐。
客戶端同樣可以驗證簽名,為了做到這點嘶是,客戶端也需要服務(wù)端的secret(密鑰)(如果這個JWT簽名是HMAC算法),或者需要服務(wù)端對公鑰(如果這個WJT是數(shù)字化簽名)钙勃。
特別注意:對于JWS,荷載(聲明部分)沒有進行加密聂喇,所以辖源,不要發(fā)送任何敏感信息。
5)關(guān)于JWE
JWE模式會對內(nèi)容加密授帕,而不是簽名同木。JWT的聲明會被加密浮梢。因此JWE帶來了保密性跛十。JWE可以被簽名并附在JWS里。這樣的話就可以同時加密和簽名秕硝。因此得到了保密性(Confidentiality)芥映、完整性(Integrity)、可認證(Authentication)。
6)那么對于客戶端奈偏,如何分辨JWS或者JWE呢坞嘀?
JWS的Header與JWE的Header是不同的,可以通過檢查“alg”Header參數(shù)的值來區(qū)分惊来。如果這個值表現(xiàn)為一個數(shù)字簽名或者MAC的算法丽涩,或者是”none“,則它是一個JWS裁蚁。
如果它表現(xiàn)為一個 Key Encryption, Key Wrapping, Direct Key Agreement, Key Agreement with Key Wrapping, or Direct Encryption algorithm矢渊。則它是一個JWE。
還可以通過Header里的“enc”(encryption algorithm)是否存在來判斷枉证,如果"enc"這個成員存在的話說明是JWE矮男,否則的話就是JWS.
7.參考
(1)10分鐘了解JSON Web令牌(JWT)
https://baijiahao.baidu.com/s?id=1608021814182894637&wfr=spider&for=pc
(2)JWT的使用流程
https://blog.csdn.net/shmely/article/details/85915044
(3)JWT全面解讀、使用步驟
https://blog.csdn.net/achenyuan/article/details/80829401
(4)JSON Web Token 入門教程 -阮一峰
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
(5)JWT(JSON Web Tokens)
https://www.cnblogs.com/zaixiuxing/p/6005968.html
(6)使用JWT實現(xiàn)單點登錄(完全跨域方案)
https://blog.csdn.net/weixin_42873937/article/details/82460997