先是學(xué)會(huì)使用jwt,然后知道她的使用流程與步驟,分析一下其原理,做到知其然,亦知其所以然。立志工具人。一起干飯!
本章主要內(nèi)容
- JWT簡(jiǎn)介
- JWT的原理和流程
- JWT 的數(shù)據(jù)結(jié)構(gòu)
1.JWT簡(jiǎn)介
- 什么是JWT
JSON Web Token(JWT)是一個(gè)開(kāi)放標(biāo)準(zhǔn)(RFC 7519),它定義了一種緊湊且自包含的方式,用于在各方之間安全地將信息作為JSON對(duì)象傳輸。由于此信息是經(jīng)過(guò)數(shù)字簽名的,因此可以被驗(yàn)證和信任。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公用/專(zhuān)用密鑰對(duì)對(duì)JWT進(jìn)行簽名。
盡管可以對(duì)JWT進(jìn)行加密以在各方之間提供保密性,但我們將重點(diǎn)關(guān)注已簽名的令牌。簽名的令牌可以驗(yàn)證其中包含的聲明的完整性,而加密的令牌則將這些聲明隱藏在其他方的面前。當(dāng)使用公鑰/私鑰對(duì)對(duì)令牌進(jìn)行簽名時(shí),簽名還證明只有持有私鑰的一方才是對(duì)其進(jìn)行簽名的一方。
-
什么時(shí)候應(yīng)該使用JSON Web令牌?
授權(quán):這是使用JWT的最常見(jiàn)方案。用戶(hù)登錄后,每個(gè)后續(xù)請(qǐng)求都將包含JWT,從而允許用戶(hù)訪問(wèn)該令牌允許的路由,服務(wù)和資源。單一登錄是當(dāng)今廣泛使用JWT的一項(xiàng)功能,因?yàn)樗拈_(kāi)銷(xiāo)很小并且可以在不同的域中輕松使用。
信息交換:JSON Web令牌是在各方之間安全地傳輸信息的一種好方法。因?yàn)榭梢詫?duì)JWT進(jìn)行簽名(例如,使用公鑰/私鑰對(duì)),所以您可以確保發(fā)件人是他們所說(shuō)的人。此外,由于簽名是使用標(biāo)頭和有效負(fù)載計(jì)算的,因此您還可以驗(yàn)證內(nèi)容是否未被篡改。
2.JWT的原理和流程
2.1 跨域認(rèn)證的問(wèn)題
互聯(lián)網(wǎng)服務(wù)離不開(kāi)用戶(hù)認(rèn)證。一般流程是下面這樣:
1、用戶(hù)向服務(wù)器發(fā)送用戶(hù)名和密碼。
2、服務(wù)器驗(yàn)證通過(guò)后,在當(dāng)前對(duì)話(session)里面保存相關(guān)數(shù)據(jù),比如用戶(hù)角色、登錄時(shí)間等等。
3、服務(wù)器向用戶(hù)返回一個(gè) session_id,寫(xiě)入用戶(hù)的 Cookie。
4、用戶(hù)隨后的每一次請(qǐng)求,都會(huì)通過(guò) Cookie,將 session_id 傳回服務(wù)器。
5、服務(wù)器收到 session_id,找到前期保存的數(shù)據(jù),由此得知用戶(hù)的身份。
這種模式的問(wèn)題在于,擴(kuò)展性(scaling)不好。單機(jī)當(dāng)然沒(méi)有問(wèn)題,如果是服務(wù)器集群,或者是跨域的服務(wù)導(dǎo)向架構(gòu),就要求 session 數(shù)據(jù)共享,每臺(tái)服務(wù)器都能夠讀取 session。
舉例來(lái)說(shuō),A 網(wǎng)站和 B 網(wǎng)站是同一家公司的關(guān)聯(lián)服務(wù)。現(xiàn)在要求,用戶(hù)只要在其中一個(gè)網(wǎng)站登錄,再訪問(wèn)另一個(gè)網(wǎng)站就會(huì)自動(dòng)登錄,請(qǐng)問(wèn)怎么實(shí)現(xiàn)?
一種解決方案是 session 數(shù)據(jù)持久化,寫(xiě)入數(shù)據(jù)庫(kù)或別的持久層。各種服務(wù)收到請(qǐng)求后,都向持久層請(qǐng)求數(shù)據(jù)。這種方案的優(yōu)點(diǎn)是架構(gòu)清晰,缺點(diǎn)是工程量比較大。另外,持久層萬(wàn)一掛了,就會(huì)單點(diǎn)失敗。
另一種方案是服務(wù)器索性不保存 session 數(shù)據(jù)了,所有數(shù)據(jù)都保存在客戶(hù)端,每次請(qǐng)求都發(fā)回服務(wù)器。JWT 就是這種方案的一個(gè)代表。
2.2 JWT 的原理
JWT 的原理是,服務(wù)器認(rèn)證以后,生成一個(gè) JSON 對(duì)象,發(fā)回給用戶(hù),就像下面這樣。
{
"姓名": "張三",
"角色": "管理員",
"到期時(shí)間": "2018年7月1日0點(diǎn)0分"
}
以后,用戶(hù)與服務(wù)端通信的時(shí)候,都要發(fā)回這個(gè) JSON 對(duì)象。服務(wù)器完全只靠這個(gè)對(duì)象認(rèn)定用戶(hù)身份。為了防止用戶(hù)篡改數(shù)據(jù),服務(wù)器在生成這個(gè)對(duì)象的時(shí)候,會(huì)加上簽名。
服務(wù)器就不保存任何 session 數(shù)據(jù)了,也就是說(shuō),服務(wù)器變成無(wú)狀態(tài)了,從而比較容易實(shí)現(xiàn)擴(kuò)展。
區(qū)別
(1) session 存儲(chǔ)在服務(wù)端占用服務(wù)器資源,而 JWT 存儲(chǔ)在客戶(hù)端
(1) session 存儲(chǔ)在 Cookie 中,存在偽造跨站請(qǐng)求偽造攻擊的風(fēng)險(xiǎn)
(2) session 只存在一臺(tái)服務(wù)器上,那么下次請(qǐng)求就必須請(qǐng)求這臺(tái)服務(wù)器,不利于分布式應(yīng)用
(3) 存儲(chǔ)在客戶(hù)端的 JWT 比存儲(chǔ)在服務(wù)端的 session 更具有擴(kuò)展性
2.3 JWT的認(rèn)證流程圖
流程說(shuō)明:
1,瀏覽器發(fā)起請(qǐng)求登陸,攜帶用戶(hù)名和密碼;
2,服務(wù)端驗(yàn)證身份,根據(jù)算法,將用戶(hù)標(biāo)識(shí)符打包生成 token,
3,服務(wù)器返回JWT信息給瀏覽器,JWT不包含敏感信息;
4,瀏覽器發(fā)起請(qǐng)求獲取用戶(hù)資料,把剛剛拿到的 token一起發(fā)送給服務(wù)器;
5,服務(wù)器發(fā)現(xiàn)數(shù)據(jù)中有 token,驗(yàn)明正身;
6,服務(wù)器返回該用戶(hù)的用戶(hù)資料;
2.4 JWT的6個(gè)優(yōu)缺點(diǎn)
1、JWT默認(rèn)不加密,但可以加密。生成原始令牌后,可以使用改令牌再次對(duì)其進(jìn)行加密。
2、當(dāng)JWT未加密方法是,一些私密數(shù)據(jù)無(wú)法通過(guò)JWT傳輸。
3、JWT不僅可用于認(rèn)證,還可用于信息交換。善用JWT有助于減少服務(wù)器請(qǐng)求數(shù)據(jù)庫(kù)的次數(shù)。
4、JWT的最大缺點(diǎn)是服務(wù)器不保存會(huì)話狀態(tài),所以在使用期間不可能取消令牌或更改令牌的權(quán)限。也就是說(shuō),一旦JWT簽發(fā),在有效期內(nèi)將會(huì)一直有效。
5、JWT本身包含認(rèn)證信息,因此一旦信息泄露,任何人都可以獲得令牌的所有權(quán)限。為了減少盜用,JWT的有效期不宜設(shè)置太長(zhǎng)。對(duì)于某些重要操作,用戶(hù)在使用時(shí)應(yīng)該每次都進(jìn)行進(jìn)行身份驗(yàn)證。
6、為了減少盜用和竊取,JWT不建議使用HTTP協(xié)議來(lái)傳輸代碼,而是使用加密的HTTPS協(xié)議進(jìn)行傳輸。
3.JSON Web Token結(jié)構(gòu)是什么?
3.1JWT消息構(gòu)成
JSON Web令牌以緊湊的形式由三部分組成,這些部分由點(diǎn)(.)分隔,分別是:
- 標(biāo)頭(header)
- 有效載荷(payload)
- 簽名(signature)
對(duì)象為一個(gè)很長(zhǎng)的字符串,字符之間通過(guò)"."分隔符分為三個(gè)子串。注意JWT對(duì)象為一個(gè)長(zhǎng)字串,各字串之間也沒(méi)有換行符,一般格式為:xxxxx.yyyyy.zzzzz 。
例如:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi5byg5LiJIiwiaWQiOjEsImV4cCI6MTYxNzM0NDM1NiwiaWF0IjoxNjE2NzM5NTU2LCJ1c2VybmFtZSI6InpoYW5nc2FuIn0.Zd9-yxbMTZvopl5sDNMRzyV8wAQQcbhd0xXwtdwkQ6U
3.2JWT 標(biāo)頭(header)
JWT的頭部承載兩部分信息:
- 聲明類(lèi)型,這里是jwt
- 聲明加密的算法 通常直接使用 HMAC SHA256
JWT里驗(yàn)證和簽名使用的算法,可選擇下面的:
JWT的頭部描述JWT元數(shù)據(jù)的JSON對(duì)象參考:
{
"alg": "HS256",
"typ": "JWT"
}
代碼樣例如下:
// header Map
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
然后,此JSON被Base64Url編碼以形成JWT的第一部分。
3.3JWT 有效載荷(payload)
Payload 部分也是一個(gè) JSON 對(duì)象,用來(lái)存放實(shí)際需要傳遞的數(shù)據(jù)。JWT 規(guī)定了7個(gè)官方字段,供選用。
- iss (issuer):簽發(fā)人
- exp (expiration time):過(guò)期時(shí)間
- sub (subject):主題
- aud (audience):受眾
- nbf (Not Before):生效時(shí)間
- iat (Issued At):簽發(fā)時(shí)間
- jti (JWT ID):編號(hào)
除以上默認(rèn)字段外,我們還可以自定義私有字段,如下例:
{
"sub": "1234567890",
"name": "dylan",
"admin": true
}
注意,JWT 默認(rèn)是不加密的,任何人都可以讀到,所以不要把秘密信息放在這個(gè)部分。
這個(gè) JSON 對(duì)象也要使用 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");
3.4JWT 簽名(signature)
Signature 部分是對(duì)前兩部分的簽名,防止數(shù)據(jù)篡改。
首先,需要指定一個(gè)密鑰(secret)。這個(gè)密鑰只有服務(wù)器才知道,不能泄露給用戶(hù)。然后,使用 Header 里面指定的簽名算法(默認(rèn)是 HMAC SHA256),按照下面的公式產(chǎn)生簽名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
算出簽名以后,把 Header、Payload、Signature 三個(gè)部分拼成一個(gè)字符串,每個(gè)部分之間用"點(diǎn)"(.)分隔,就構(gòu)成整個(gè)JWT對(duì)象TOKEN, 就可以返回給用戶(hù)。
3.4.1 Base64URL算法
前面提到,Header 和 Payload 串型化的算法是 Base64URL。這個(gè)算法跟 Base64 算法基本類(lèi)似,但有一些小的不同。
JWT 作為一個(gè)令牌(token),有些場(chǎng)合可能會(huì)放到 URL(比如 api.example.com/?token=xxx)。Base64 有三個(gè)字符+、/和=,在 URL 里面有特殊含義,所以要被替換掉:=被省略、+替換成-,/替換成_ 。這就是 Base64URL 算法。
3.5 JWT的用法
客戶(hù)端接收服務(wù)器返回的JWT,將其存儲(chǔ)在Cookie或localStorage中。
此后,客戶(hù)端將在與服務(wù)器交互中都會(huì)帶JWT。如果將它存儲(chǔ)在Cookie中,就可以自動(dòng)發(fā)送,但是不會(huì)跨域,因此一般是將它放入HTTP請(qǐng)求的Header Authorization字段中。
Authorization: Bearer <token>
當(dāng)跨域時(shí),也可以將JWT被放置于POST請(qǐng)求的數(shù)據(jù)主體中。