Json Web Token 個(gè)人見(jiàn)解

jwt(Json Web token) 是一種用于不同組織之間交換數(shù)據(jù)的格式。既然名稱(chēng)中包含了Json,顯而易見(jiàn),其數(shù)據(jù)結(jié)構(gòu)是以Json為基礎(chǔ)進(jìn)行搭建的。數(shù)據(jù)中包含了三個(gè)部分,header、payload以及token。其中token可以使用私鑰(HMAC算法),也可以基于公私鑰的形式,通過(guò)對(duì)header及payload進(jìn)行計(jì)算生成,從而達(dá)到校驗(yàn)header及payload數(shù)據(jù)合法性的作用。

使用場(chǎng)景:

  • 跨域認(rèn)證。單點(diǎn)登錄系統(tǒng)是最常見(jiàn)的jwt使用場(chǎng)景。用戶(hù)在登錄后,系統(tǒng)返回jwt的信息。后續(xù)的所有請(qǐng)求都會(huì)帶上jwt的信息,從而認(rèn)證該用戶(hù)的信息。由于數(shù)據(jù)的請(qǐng)求可以通過(guò)header、cookies、參數(shù)等方式,使得跨域訪(fǎng)問(wèn)變得十分簡(jiǎn)單。
  • 跨域數(shù)據(jù)分享。通過(guò)使用公私鑰的方法,客戶(hù)端可以使用公鑰對(duì)簽發(fā)的數(shù)據(jù)進(jìn)行校驗(yàn),從而保證數(shù)據(jù)不被篡改。

結(jié)構(gòu)

? jwt包含了三個(gè)部分

  • header
  • payload
  • signature

從結(jié)構(gòu)上來(lái)看,三個(gè)部分都為base64-URL編碼后,使用.拼接后的數(shù)據(jù):

? eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

? eyJpc3MiOiJzdW1tZXJAdGVuY2VudCIsImFkbWluIjoiRmFsc2UiLCJleHAiOjE1MzkwNTMxODV9.

? Tc8VyoGFihW_CBXnCIX6eHzbLo3WEQH-Sy65ohSWfqM

header

? header包含了typalg兩個(gè)部分:

{
  "alg": "HS256",  // 計(jì)算的方法
  "typ": "JWT"     // 都為JTW
}

payload

payload則包含了本次聲明的數(shù)據(jù)。數(shù)據(jù)分為幾個(gè)類(lèi)型

  • jwt協(xié)議中定義的聲明
    • iss 簽發(fā)人
    • sub 主題
    • aud 受眾
    • exp 過(guò)期時(shí)間
    • nbf 生效時(shí)間
    • jti jwt id
  • 通用的數(shù)據(jù)
  • 私有的數(shù)據(jù): 用戶(hù)自己定義的數(shù)據(jù)

一個(gè)payload的樣例:

{
    "sub": "userinfo",
    "name": "testuser",
    "admin": false
}

signature

signature是對(duì)headerpayload的數(shù)據(jù)進(jìn)行簽名。簽名的算法可以使用HMAC RSA256,也可以使用RSA256.前者需要所有的使用者都擁有加密的secret,后者的使用者則需要RSA的public key用于數(shù)據(jù)的解密。

signature的算法通常如下:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

使用

從上面的定義來(lái)看,jwt本身是十分簡(jiǎn)單的。而且各種語(yǔ)言的JSON Parser都很全,所以相應(yīng)的庫(kù)也十分全面,基本常見(jiàn)的語(yǔ)言都是支持的。而且一種語(yǔ)言都有非常多的輪子,具體可以參考。

這里就簡(jiǎn)單的以python作為樣例,使用的是pyjwt,文檔請(qǐng)參考

HMAC

import jwt
key = 'secret'
encoded = jwt.encode({'sub': '123456', 'user': 'testuser'}, key, algorithm='HS256')
decoded = jwt.decode(encoded, key, algorithms='HS256')

RSA

RSA算法的用法和HMAC類(lèi)似

import jwt
encoded = jwt.encode({'sub': 123456, 'user': 'testuser'}, pri, algorithm='RS256')
decoded = jwt.decode(encoded, pub)

交互

How does a JSON Web Token work

jwt適用的場(chǎng)景是跨域的數(shù)據(jù)認(rèn)證。通常的使用流程如下:

  1. client需要訪(fǎng)問(wèn)服務(wù)資源,首先請(qǐng)求認(rèn)證服務(wù)器,通過(guò)相應(yīng)的參數(shù),獲取合法的jwt token
  2. client在后續(xù)所有的訪(fǎng)問(wèn)中,都在請(qǐng)求包帶上jwt token
  3. 資源的服務(wù)器校驗(yàn)該jwttoken是否合法。由于jwt的特性,校驗(yàn)可以在服務(wù)器本地完成,而不需要去請(qǐng)求遠(yuǎn)端的認(rèn)證服務(wù)器。

請(qǐng)求時(shí),帶參數(shù)的方式有多種:

  1. cookies

    如果認(rèn)證服務(wù)器與資源服務(wù)器是同域的,則可以把jwt token放在cookies進(jìn)行攜帶。

  2. header

    對(duì)于跨域的情況,則可以在請(qǐng)求的header中添加Authorization: bearer <token>字段的方式來(lái)傳遞。由于是在header中添加數(shù)據(jù),所以沒(méi)有跨域的cookies無(wú)法攜帶的問(wèn)題。

  3. params

    還有就是在請(qǐng)求的參數(shù)中增加token字段。比如Get方法中的url&token=xxx的形式,或者是Post中的請(qǐng)求參數(shù)

tips

使用claim校驗(yàn)

jwt官方定義了幾種常用的claim,用于對(duì)數(shù)據(jù)合法性進(jìn)行校驗(yàn).常用的有:

  • exp: token的過(guò)期時(shí)間。

    服務(wù)端或者客戶(hù)端進(jìn)行decode時(shí),可以自動(dòng)捕獲超時(shí)錯(cuò)誤。

    jwt.encode({'exp': datetime.utcnow()}, 'secret')
    try:
        jwt.decode('JWT_STRING', 'secret', algorithms=['HS256'])
    except jwt.ExpiredSignatureError:
        # Signature has expired
    
  • nbf: token的啟用時(shí)間

  • iss: token的簽發(fā)者

  • aud: token的接受者

stateful & unstateful

stateful和unstateful是由jwt中數(shù)據(jù)存儲(chǔ)的內(nèi)容決定的. 如果jwt中的數(shù)據(jù)包含了所需要的全部數(shù)據(jù), 每個(gè)client在使用數(shù)據(jù)時(shí), 不需要再到某個(gè)服務(wù)中進(jìn)行合法性校驗(yàn), 則這個(gè)jwt是unstateful的, 反之, 則是stateful的.

舉個(gè)例子:

{
    "uid": 123456,
    "exp": 1539148957,
    "group": "admin",
    "company": "1"
}

上面這個(gè)jwt包含了uid, group, company字段. 這些數(shù)據(jù)已經(jīng)足夠處理后續(xù)的所有請(qǐng)求了. 因此, client在執(zhí)行的時(shí)候, 不需要再請(qǐng)求一次服務(wù)器, 獲取剩余的數(shù)據(jù). 這種jwt, 可以稱(chēng)之為 unstateful的.

再看下面這個(gè):

{
    "uid": "sess-1234-412-x12",
    "exp": 1539148957
}

這個(gè)jwt中僅僅包含了一個(gè)uid信息, 所有的數(shù)據(jù)都沒(méi)有. 在使用時(shí), 還需要再請(qǐng)求一個(gè)服務(wù)獲取相應(yīng)的數(shù)據(jù), 這種jwt就是stateful的.

當(dāng)然, stateful的jwt, 也可以包含所有的數(shù)據(jù).

{
    "uid": 123456,
    "exp": 1539148957,
    "group": "admin",
    "company": "1",
    "tid": "123-xx-xx"
}

在使用該jwt之前, 需要首先校驗(yàn)該tid是否還是合法. 一般會(huì)在一個(gè)服務(wù)器中維護(hù)一個(gè)tid的黑名單. 如果該tid屬于黑名單中, 則強(qiáng)制廢除該jwt.

安全問(wèn)題

雖然token看起來(lái)是一段加密后的數(shù)據(jù), 而且還用了加密算法, 但實(shí)際上并不是加密的. 回顧下token的樣子,

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.

eyJzdWIiOjEyMzQ1NiwidXNlciI6InRlc3R1c2VyIn0.

NAHaQSXelvub7_y3XSXpofBQDuzBrGGxeJ4SAQPkYRE

token分成了三段:

  • header
  • payload
  • token

其中, header\payload都是僅僅用base64-URL encode后的結(jié)果. 也就是說(shuō),誰(shuí)都可以做decode獲取數(shù)據(jù). 所以, jwt中一定不能包含涉密的數(shù)據(jù),尤其是用戶(hù)密碼等敏感信息

性能問(wèn)題

這里的性能問(wèn)題, 指的是該結(jié)構(gòu)帶來(lái)的請(qǐng)求數(shù)據(jù)變多的問(wèn)題. 在jwt的使用場(chǎng)景中, 所有的請(qǐng)求都需要完整帶上jwt的數(shù)據(jù). 由于payload這部分的數(shù)據(jù)僅僅是base64后的數(shù)據(jù), 并沒(méi)有做任何處理. 所以, 如果在payload中增加過(guò)多的數(shù)據(jù), 就會(huì)導(dǎo)致jwt的結(jié)構(gòu)變大, 從而導(dǎo)致請(qǐng)求的效率降低.

替代session

jwt的競(jìng)爭(zhēng)對(duì)手并不是傳統(tǒng)的session, 而是其他的跨域認(rèn)證方案, 例如SAML以及SWT. 但是, 也有很多人使用jwt作為session的替代品進(jìn)行使用. 選擇jwt的原因大多數(shù)如下:

  • 平行擴(kuò)容

    使用session的服務(wù)在用了jwt后, 完全可以在本地對(duì)jwt發(fā)過(guò)來(lái)的數(shù)據(jù)進(jìn)行校驗(yàn), 從而不需要傳統(tǒng)的session記錄. 因此, 當(dāng)服務(wù)快速平行擴(kuò)容時(shí), 也不會(huì)因?yàn)閟ession記錄找不到而引起問(wèn)題.

  • 免去session服務(wù)器

    同樣的, 因?yàn)閖wt已經(jīng)記錄了用戶(hù)相關(guān)的信息, 也就不需要再去session獲取一次用戶(hù)的數(shù)據(jù). 從而避免session服務(wù)器成為卡點(diǎn).

當(dāng)然, 這兩點(diǎn)優(yōu)勢(shì)也飽受質(zhì)疑.

首先, 現(xiàn)在session大多都直接存放在redis, memcache等緩存中, 已經(jīng)沒(méi)有什么人使用本地的文件存儲(chǔ)了. 此外, 也可以通過(guò)負(fù)載均衡來(lái)保證訪(fǎng)問(wèn)都單用戶(hù)請(qǐng)求都落在單一服務(wù)器上.

而對(duì)于免去session服務(wù)器, 則要看如何定義jwt中的數(shù)據(jù). 前面說(shuō)過(guò), jwt可以是unstateful或者是stateful的. 對(duì)于unstateful的jwt, 當(dāng)?shù)卿浄?wù)器簽發(fā)了之后, 該jwt只要在exp之前都長(zhǎng)期有效. 所以服務(wù)端是無(wú)法知道當(dāng)前有多少用戶(hù)在線(xiàn). 這對(duì)于某些應(yīng)用是不合適的. 而且, 廢除jwt也成為了件難事. 如果需要廢除已經(jīng)簽發(fā), 但仍在有效期的jwt, 就需要添加一個(gè)黑名單, 那就變成了stateful的jwt, 而且也需要一個(gè)集中化的服務(wù).

結(jié)論

jwt在2015年就已經(jīng)成為RFC標(biāo)準(zhǔn), 其token生成的設(shè)計(jì), 很好的解決了不同組織之間相互數(shù)據(jù)信任的問(wèn)題, 因而在單點(diǎn)認(rèn)證方面得到了大規(guī)模的應(yīng)用.

在單體服務(wù)中, jwt也被很多人用來(lái)作為session的替代品進(jìn)行使用, 也引來(lái)了很多的討論, 甚至是互噴. 整體上來(lái)說(shuō), 經(jīng)過(guò)設(shè)計(jì), 對(duì)于某些場(chǎng)景下, 由于簽發(fā)的數(shù)據(jù)天然就帶了可信任的token, 所以不需要再進(jìn)行遠(yuǎn)端校驗(yàn), 確實(shí)是能提高系統(tǒng)整體的吞吐率.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • JWT 介紹 (https://jwt.io/) JSON Web Token(JWT)是一個(gè)開(kāi)放標(biāo)準(zhǔn)(RFC 7...
    匆匆歲月閱讀 5,768評(píng)論 1 41
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,544評(píng)論 19 139
  • 作者:阮一峰www.ruanyifeng.com/blog/2018/07/json_web_token-tuto...
    grain先森閱讀 5,898評(píng)論 0 21
  • 文章轉(zhuǎn)自:http://www.ruanyifeng.com/blog/2018/07/json_web_toke...
    daos閱讀 877評(píng)論 0 3
  • 1. 微服務(wù)架構(gòu)介紹 1.1 什么是微服務(wù)架構(gòu)? 形像一點(diǎn)來(lái)說(shuō),微服務(wù)架構(gòu)就像搭積木,每個(gè)微服務(wù)都是一個(gè)零件,并使...
    靜修佛緣閱讀 6,808評(píng)論 0 39

友情鏈接更多精彩內(nèi)容