JSON Web Token 是什么?


免費獲得官方JWT手冊並深入學習JWT吧!

簡介

JSON Web Token(縮寫JWT),是一套開放的標准(RFC 7519),它定義了一種緊湊且自URL安全的方式,以JSON對象的方式在各方之間安全地進行信息傳輸。由於此信息是經過數字簽名的,因此是可以被驗證和信任的。可以使用密鑰(secret)(使用HMAC算法)或者使用RSA或ECDSA的公有/私有密鑰對JWT進行簽名。

雖然可以對JWT進行加密用來在各方之間提供保密性,但我們還是重點關注下簽名的令牌(token)本身,簽名的令牌可以驗證其中包含的聲明的完整性,而加密的令牌則將這些聲明在其他方的面前進行隱藏,以提供安全性。當使用公鑰/私鑰對對令牌進行簽名時,簽名還可以證明只有持有私鑰的一方才是對其進行簽名的一方。

JWT應用場景

一些常用到JWT的情況:

  • 授權:這是使用JWT的最常見方案。一旦用戶登錄,每個后續請求將包括JWT,從而允許用戶訪問該令牌允許的路由,服務和資源。單點登錄是當今廣泛使用JWT的一項功能,因為它的開銷很小並且支持跨域。
  • 信息交換:JWT是在各方之間安全地傳輸信息的好方法。因為可以對JWT進行簽名(例如,使用公鑰/私鑰對)。此外,由於簽名是使用Header和Payload計算的,因此還可以驗證內容是否遭到篡改。

JWT數據結構

JSON Web Token以緊湊的形式由三部分組成,這些部分由點(.)分隔,分別是:

  • Header
  • Payload
  • Signature

大致格式如下所示:

xxxxx.yyyyy.zzzzz

下面來看下具體每個部分:

Header通常由兩部分組成的一個JSON對象:令牌的類型(即JWT)和所使用的簽名算法,例如HMAC SHA256或RSA。例如:

{
  "alg": "HS256",
  "typ": "JWT"
}

最后,將上面的 JSON 對象使用Base64URL編碼(轉成字符串作為第一部分。

Payload

第二部分是包含聲明的Payload(有效載荷),聲明是有關實體(通常是用戶信息)以及一些額外數據的說明,用來傳遞實際的數據。聲明有三種類型:

  • 標准注冊的聲明:這些是一組非強制性的但建議使用的預定義字段集,用來提供有用、可互操作的聲明。

以下官方提供的一些字段,完整字段可查看標准文檔
iss: jwt簽發者
sub: jwt所面向的用戶
aud: 接收jwt的一方
exp: jwt的過期時間,這個過期時間必須要大於簽發時間
nbf: 定義在什么時間之前,該jwt都是不可用的.
iat: jwt的簽發時間
jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。

注意:以上聲明的字段僅是三個字符,因為JWT是緊湊的。

  • 公共的聲明:這些聲明可以由使用JWT的人進行自定義. 但是為了避免沖突,這些聲明應該在IANA JSON Web Token Registry中定義過,或者被定義為包含一個抗沖突名稱空間的URI

  • 私有的聲明:這些是自定義的聲明,用於在雙方同意使用它們的情況下共享信息,而不是注冊或公開聲明。一般不建議存放敏感信息,因為base64是對稱解密的,意味着該部分信息可以歸類為明文信息。

例如,定義一個payload:

{
  "sub": "1234567890", // 標准聲明
  "name": "John Doe", // 公共聲明
  "admin": true  // 自定義
}

然后,同樣地對payload進行Base64Url編碼,作為JSON Web Token的第二部分。

對於已簽名的令牌(token),盡管信息可以防止篡改,但任何人都可以讀取。除非將其加密,否則請勿將敏感信息放入JWT的Payload或Header元素中。

Signature

Signature 部分是對前兩部分的簽名,防止數據篡改。
要生成簽名部分,需求根據編碼的Header,編碼的Payload,一個指定的密鑰(secret),通過Header指定的算法(默認是HMAC SHA256算法),然后生成簽名。
例如,如果要使用HMAC SHA256算法,則將通過以下方式創建簽名:

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

生成JWT

最后把 Header、Payload、Signature 三個部分拼成一個字符串,每個部分之間用"點"(.)分隔,就可以返回給用戶。
輸出是三個由點分隔的Base64-URL字符串,可以在HTML和HTTP環境中輕松傳遞這些字符串,與基於XML的標准(例如SAML)相比,它更緊湊。
下面顯示了一個JWT,它已對先前的Header和Payload進行了編碼,並用一個密鑰(secret)進行簽名。
image

如果你想實踐下,可以使用jwt.io.debugger在線解碼、驗證及生成JWT。
image

Base64URL

前面提到,Header 和 Payload 串型化的算法是 Base64URL。這個算法跟 Base64 算法基本類似,但有一些小的不同。

JWT 作為一個令牌(token),有些場合可能會放到 URL(比如 api.example.com/?token=xxx)。Base64 有三個字符+、/和=,在 URL 里面有特殊含義,所以要被替換掉:=被省略、+替換成-,/替換成_ 。這就是 Base64URL 算法。

JWT如何使用?

在身份驗證中,當用戶使用其憑據成功登錄后,將返回JSON Web Token。由於令牌(token)是憑據,因此必須格外小心以防止安全問題。通常,令牌的有效時間不應超過要求的時間。
由於缺乏安全性,也不應該將敏感的會話數據存儲在瀏覽器中。
當用戶想訪問受保護的路由或者資源時,用戶代理(比如瀏覽器)需要帶上JWT,一般是在請求頭里加入Authorization,並加上Bearer標注:

headers: {
    'Authorization': 'Bearer ' + token
}

在某些情況下,這可以是無狀態授權機制。服務器的受保護路由將在Authorization標頭中檢查有效的JWT ,如果存在,則將允許用戶訪問受保護的資源。如果JWT包含必要的數據,則可以減少查詢數據庫中某些操作的需求,盡管這種情況並非總是如此。

如果令牌是在Authorization標頭中發送的,則不會有跨域資源共享(CORS)的問題,因為不使用cookie。
另一種做法是,跨域的時候,JWT 就放在 POST 請求的數據體里面。
下圖顯示了如何獲取JWT並將其用於訪問API或資源:
image

1、應用程序或客戶端向授權服務器請求授權。
2、授予授權后,授權服務器會將訪問令牌返回給應用程序。
3、該應用程序使用訪問令牌來訪問受保護的資源(例如API)。

為什么要使用JWT?

SWT和SAML對比

讓我們談談與簡單Web令牌(Simple Web Tokens,SWT)和 安全性聲明標記語言令牌(Security Assertion Markup Language Tokens,SAML)相比,JSON Web令牌(JWT)的好處。

由於JSON不如XML冗長,在編碼時JSON的大小也較小,從而使JWT比SAML更為緊湊。這使得JWT是在HTML和HTTP環境中傳遞的不錯的選擇。

在安全方面,SWT只能使用HMAC算法的共享密鑰對其進行對稱簽名。但是,JWT和SAML令牌可以使用X.509證書形式的公用/專用密鑰對進行簽名。與簽名JSON的簡單性相比,使用XML數字簽名對XML進行簽名而不引入一些的安全漏洞是非常困難的。

JSON解析器在大多數編程語言中都很常見,因為它們直接映射到對象。相反,XML沒有自然的文檔到對象映射。與SAML斷言相比,JWT更加簡單易用。

編碼的JWT和編碼的SAML的長度比較:
image

傳統Session/Cookie的認證對比

基於Cookie的身份認證

身份認證的一般流程:
image

但是http是無狀態協議,這就意味着如果用戶向我們的應用提供了用戶名和密碼來進行用戶認證,那么下一次請求時,用戶還要再一次進行用戶認證才行,因為根據http協議,我們並不能知道是哪個用戶發出的請求。
傳統的解決方案是創建所謂的“會話(Session)”,會話分為兩個部分:
1、存儲在服務端上的對象,用於記住用戶是否仍在登錄,對其個人資料的引用等。
2、客戶端上的cookie,其中存儲了某種ID,可以在服務器上根據會話對象的ID對其進行引用。
身份驗證過程如下:
image

通過從登錄頁面向服務端發送用戶名/密碼組合來創建新會話。如果服務器可以使用該用戶名和密碼來匹配用戶,它將在服務器上生成一個新的會話對象,並在客戶端上設置一個帶有該會話ID的cookie。后續用戶發出的請求需要帶上cookie,服務端就可以驗證是哪個用戶以及驗證會話是否有效。

基於Cookie認證的缺點

以上模式在單機上沒有問題。問題在於擴展性不好,存在以下問題:

  • 多個服務端:當使用多個后端進行身份驗證時,事情可能會變得復雜(例如,可以通過中央應用服務器代理所有請求,然后需要了解每個輔助服務的所有邏輯,或者每個服務都可以實現復雜的服務器間通信(和CORS),以使用中央身份驗證服務器驗證傳入的會話ID。在任何一種情況下,都需要在應用服務器上增加額外的負載,並且需要維護更復雜的互連)。
  • 會話:需要存儲在內存,數據庫或Redis之類的鍵值存儲中的某個位置;並且需要對其進行管理,以使其在到期或無效時被刪除。
  • 可伸縮性差:擴展服務器時需要擴展會話存儲,增加了復雜性。
  • 性能問題:當會話需要存儲在服務器上時,每個數據庫請求都需要進行很多數據庫/存儲查找,這會使服務器陷入癱瘓。
  • CSRF:如果正在使用cookie,則需要額外的安全性來防止跨站點請求攻擊,因為cookie會隨該站點的任何請求一起自動發送到服務器。
  • CORS:Cookie + CORS在不同的域中表現不佳(實際上,真正的跨域根本不起作用)。

而使用JWT,你可以使用應用程序注冊自己(與使用老式應用程序幾乎相同),然后使用憑據(例如,用戶名/密碼或第三方OAuth)登錄。但是服務器不會進行會話和設置Cookie,而是向你發送JSON Web令牌。然后,你可以使用該令牌來完成對服務器的任何操作。
image

后記

以上譯文僅用於學習交流,水平有限,難免有錯誤之處,敬請指正。

鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM