1. 程式人生 > 其它 >JSON Web令牌(JWT)詳解

JSON Web令牌(JWT)詳解

1. 摘要

JSON Web Token(縮寫 JWT)是目前最流行的跨域認證解決方案,本文介紹它的原理,用法和詳細的資料結構。

2. JWT的定義

Json web token(JWT)是為了網路應用環境間傳遞宣告而執行的一種基於JSON的開發標準(RFC 7519),該token被設計為緊湊且安全的,特別適用於分散式站點的單點登陸(SSO)場景。JWT的宣告一般被用來在身份提供者和服務提供者間傳遞被認證的使用者身份資訊,以便於從資源伺服器獲取資源,也可以增加一些額外的其它業務邏輯所必須的宣告資訊,該token也可直接被用於認證,也可被加密。

什麼情況下使用JWT比較適合? 授權這是最常見的使用場景,解決單點登入問題。因為JWT使用起來輕便,開銷小,服務端不用記錄使用者狀態資訊(無狀態),所以使用比較廣泛; 資訊交換:

JWT是在各個服務之間安全傳輸資訊的好方法。因為JWT可以簽名,例如,使用公鑰/私鑰對兒 - 可以確定請求方是合法的。此外,由於使用標頭和有效負載計算簽名,還可以驗證內容是否未被篡改。

3. JWT的原理和流程

3.1 跨域認證的問題

網際網路服務離不開使用者認證。一般流程是下面這樣:

1、使用者向伺服器傳送使用者名稱和密碼。 

2、伺服器驗證通過後,在當前對話(session)裡面儲存相關資料,比如使用者角色、登入時間等等。 

3、伺服器向用戶返回一個 session_id,寫入使用者的 Cookie。 

4、使用者隨後的每一次請求,都會通過 Cookie,將 session_id 傳回伺服器。 

5、伺服器收到 session_id,找到前期儲存的資料,由此得知使用者的身份。

這種模式的問題在於,擴充套件性(scaling)不好。單機當然沒有問題,如果是伺服器叢集,或者是跨域的服務導向架構,就要求 session 資料共享,每臺伺服器都能夠讀取 session。

舉例來說,A 網站和 B 網站是同一家公司的關聯服務。現在要求,使用者只要在其中一個網站登入,再訪問另一個網站就會自動登入,請問怎麼實現?

一種解決方案是 session 資料持久化,寫入資料庫或別的持久層。各種服務收到請求後,都向持久層請求資料。這種方案的優點是架構清晰,缺點是工程量比較大。另外,持久層萬一掛了,就會單點失敗。 另一種方案是伺服器索性不儲存 session 資料了,所有資料都儲存在客戶端,每次請求都發回伺服器

。JWT 就是這種方案的一個代表。

3.2 JWT 的原理

JWT 的原理是,伺服器認證以後,生成一個 JSON 物件,發回給使用者,就像下面這樣。

 {
 "姓名": "張三",
 "角色": "管理員",
 "到期時間": "2018年7月1日0點0分"
 }

以後,使用者與服務端通訊的時候,都要發回這個 JSON 物件。伺服器完全只靠這個物件認定使用者身份。為了防止使用者篡改資料,伺服器在生成這個物件的時候,會加上簽名。

伺服器就不儲存任何 session 資料了,也就是說,伺服器變成無狀態了,從而比較容易實現擴充套件。

區別:

 (1) session 儲存在服務端佔用伺服器資源,而 JWT 儲存在客戶端
 (1) session 儲存在 Cookie 中,存在偽造跨站請求偽造攻擊的風險
 (2) session 只存在一臺伺服器上,那麼下次請求就必須請求這臺伺服器,不利於分散式應用
 (3) 儲存在客戶端的 JWT 比儲存在服務端的 session 更具有擴充套件性

3.3 JWT的認證流程圖

流程說明:
 1,瀏覽器發起請求登陸,攜帶使用者名稱和密碼;
 2,服務端驗證身份,根據演算法,將使用者識別符號打包生成 token,
 3,伺服器返回JWT資訊給瀏覽器,JWT不包含敏感資訊;
 4,瀏覽器發起請求獲取使用者資料,把剛剛拿到的 token一起傳送給伺服器;
 5,伺服器發現數據中有 token,驗明正身;
 6,伺服器返回該使用者的使用者資料;

3.4 JWT的6個優缺點

1、JWT預設不加密,但可以加密。生成原始令牌後,可以使用改令牌再次對其進行加密。

2、當JWT未加密方法是,一些私密資料無法通過JWT傳輸。

3、JWT不僅可用於認證,還可用於資訊交換。善用JWT有助於減少伺服器請求資料庫的次數。

4、JWT的最大缺點是伺服器不儲存會話狀態,所以在使用期間不可能取消令牌或更改令牌的許可權。也就是說,一旦JWT簽發,在有效期內將會一直有效。

5、JWT本身包含認證資訊,因此一旦資訊洩露,任何人都可以獲得令牌的所有許可權。為了減少盜用,JWT的有效期不宜設定太長。對於某些重要操作,使用者在使用時應該每次都進行進行身份驗證。

6、為了減少盜用和竊取,JWT不建議使用HTTP協議來傳輸程式碼,而是使用加密的HTTPS協議進行傳輸。

4. JWT 的資料結構

4.1 JWT訊息構成

一個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元資料的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 物件,用來存放實際需要傳遞的資料。JWT 規定了7個官方欄位,供選用。

 iss (issuer):簽發人
 exp (expiration time):過期時間
 sub (subject):主題
 aud (audience):受眾
 nbf (Not Before):生效時間
 iat (Issued At):簽發時間
 jti (JWT ID):編號

除以上預設欄位外,我們還可以自定義私有欄位,如下例:

{
 "sub": "1234567890",
 "name": "chenyang",
 "admin": true
 }

注意,JWT 預設是不加密的,任何人都可以讀到,所以不要把祕密資訊放在這個部分。 這個 JSON 物件也要使用 Base64URL 演算法轉成字串。 程式碼樣例如下:

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 部分是對前兩部分的簽名,防止資料篡改。 首先,需要指定一個金鑰(secret)。這個金鑰只有伺服器才知道,不能洩露給使用者。然後,使用 Header 裡面指定的簽名演算法(預設是 HMAC SHA256),按照下面的公式產生簽名。

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

算出簽名以後,把 Header、Payload、Signature 三個部分拼成一個字串,每個部分之間用"點"(.)分隔,就構成整個JWT物件TOKEN, 就可以返回給使用者。

4.4.1 Base64URL演算法

前面提到,Header 和 Payload 串型化的演算法是 Base64URL。這個演算法跟 Base64 演算法基本類似,但有一些小的不同。 JWT 作為一個令牌(token),有些場合可能會放到 URL(比如 api.example.com/?token=xxx)。Base64 有三個字元+、/和=,在 URL 裡面有特殊含義,所以要被替換掉:=被省略、+替換成-,/替換成_ 。這就是 Base64URL 演算法。

4.5 JWT的用法

客戶端接收伺服器返回的JWT,將其儲存在Cookie或localStorage中。 此後,客戶端將在與伺服器互動中都會帶JWT。如果將它儲存在Cookie中,就可以自動傳送,但是不會跨域,因此一般是將它放入HTTP請求的Header Authorization欄位中。

 Authorization: Bearer <token>

當跨域時,也可以將JWT被放置於POST請求的資料主體中。

5. JWT、JWS、JWE的區別

1)JWT(JSON Web Tokens),jwt長度較小,且可以使用URL傳輸(URL safe)。不想cookies只能在web環境起作用。 JWT可以同時使用在web環境和RESTfull的介面。

2)對於開發者來說,JWT與另外兩種相近的標準:JWS(JSON Web Signature)、JWE(JSON Web Encryption),容易引起混亂。

3)關於JWT的描述,可以參考RFC7519(https://tools.ietf.org/html/rfc7519)的描述: **JSON Web Token (JWT) **是一個間接地、URL安全的,表現為一組宣告,可以在雙方之間進行傳輸。一個JWT的宣告,是指經過編碼後的一個JSON物件,這個JSON物件可以是一個JSON Web Signature(JWS)結構的荷載(payload),或者是一個JSON Web Encryption(JWE)結構的明文。允許使用宣告進行數字簽名,或者通過一個Message Authentication Code(MAC)進行完整性保護可選擇是否加密。

簡單來說,JWTs表現為一組被編碼為JWS and/or JWE結構的JSON object的宣告(Claim).

換言之,一組JWT宣告(就是表現為JSON格式的Claims)被通過JWS結構或者JWE結構(或者同時使用兩種)傳送,決定於你如何去實現它。所以,當你傳送JWT給別人是,它實際上是一個JWT荷載或者JWE荷載。JWS荷載更加常用。

4)關於JWS 顧名思義,JWS模式對這個內容進行了數字化簽名。這個內容被用來存放JWT的宣告.服務端簽名出JWT並且傳送到客戶端,並在使用者成功認證後進行應答。伺服器期望客戶端在下次請求的時候將JWS作為請求的一部分,傳送回服務端。

如果我們處理的客戶端是欺騙者怎麼辦呢?這就是簽名(signature)需要出場的地方了。簽名攜帶了完整的可驗證的資訊。換句話說,伺服器可以確認,接收到的JWT聲明裡的JWS是沒有經過欺騙客戶端、中間者進行修改的。 服務端通過驗證訊息的簽名來確保客戶端沒有修改宣告。如果服務端檢測到任何修改,可以採取適當的動作(拒絕這次請求或者鎖定客戶端之類的)。 客戶端同樣可以驗證簽名,為了做到這點,客戶端也需要服務端的secret(金鑰)(如果這個JWT簽名是HMAC演算法),或者需要服務端對公鑰(如果這個WJT是數字化簽名)。 特別注意:對於JWS,荷載(宣告部分)沒有進行加密,所以,不要傳送任何敏感資訊

5)關於JWE JWE模式會對內容加密,而不是簽名。JWT的宣告會被加密。因此JWE帶來了保密性。JWE可以被簽名並附在JWS裡。這樣的話就可以同時加密和簽名。因此得到了保密性(Confidentiality)、完整性(Integrity)、可認證(Authentication)

6)那麼對於客戶端,如何分辨JWS或者JWE呢? JWS的Header與JWE的Header是不同的,可以通過檢查“alg”Header引數的值來區分。如果這個值表現為一個數字簽名或者MAC的演算法,或者是”none“,則它是一個JWS。 如果它表現為一個 Key Encryption, Key Wrapping, Direct Key Agreement, Key Agreement with Key Wrapping, or Direct Encryption algorithm。則它是一個JWE。 還可以通過Header裡的“enc”(encryption algorithm)是否存在來判斷,如果"enc"這個成員存在的話說明是JWE,否則的話就是JWS.

JWT好文