原文做者:Mikey Stecky-Efantis
原文地址:5 Easy Steps to Understanding JSON Web Tokens(JWT)
譯者:命名最頭疼html
在本文中, 將解釋JSON Web Tokens(JWT)的基本原理以及使用他們的緣由。JWT 是確保你應用程序信任和安全的重要部分。JWT 容許以安全的方式來聲明,例如用戶數據。git
爲了解釋JWT如何工做,讓咱們從一個抽象的定義開始。github
一個 JSON Web Token(JWT)是一個 JSON 對象,在 RFC7519 中定義爲表示兩方之間的一組信息的安全方式。該令牌由標頭,有效負載和簽名組成。 簡單來講,JWT 只是一個具備如下格式的字符串web
header.payload.signature
複製代碼
應該注意的是,雙引號字符串實際上被視爲有效的 JSON 對象。算法
下面將展現實際使用 JWT 的方式和緣由,咱們將使用一個簡單的例子(以下圖所示),這個例子中的實體是用戶,應用服務器,和認證服務器。認證服務器將提供 JWT 給用戶,經過 JWT,用戶能夠安全的和應用服務器間進行通信。json
在這個例子中,用戶第一次進入認證服務器並使用認證服務器登錄系統(例:在 Facebook 和 Google 中經過用戶名和密碼登錄,等)。認證服務器建立JWT而且發生給用戶,當用戶對應用程序進行 API 調用時,JWT將隨着API一併傳遞。在此配置中,應用程序服務器將會進行認證配置,用於驗證傳入的JWT是不是由身份服務器建立的(稍後將詳細解釋驗證過程)。所以,當用戶使用帶有JWT的API去發起調用請求時,該應用可以使用JWT去認證這個API是否來自被認證的用戶。如今,將更深刻地研究JWT自己及其構建和認證的方式。瀏覽器
JWT 的頭部包含有關如何計算 JWT 簽名的信息,其標頭是如下格式的 JSON 對象安全
{
"typ": "JWT",
"alg": "HS256"
}
複製代碼
在上述 JSON 中,"typ" 鍵值指定了 JWT 對象,"alg"鍵值指定使用哪一種散列算法來建立 JWT 簽名組件。在這個例子中,咱們使用 HMAC-SHA256 算法(一種使用密鑰的散列算法)來計算簽名(在步驟 3 中更詳細地討論)。bash
PayLoad是存儲在 JWT 裏的內部數據(該數據也稱爲 JWT 的 「聲明」)。在這個例子中,認證服務器建立一個JWT 用於存儲用戶信息,特別是用戶ID。服務器
{
"userId": "b08f86af-35da-48f2-8fab-cef3904660bd"
}
複製代碼
在這個例子中,咱們只將一個聲明放入 payload 中,你也能夠根據須要添加任意數量的聲明。JWT關鍵信息(payload)有幾種不一樣的標準聲明,例如 "iss" 表示 issuer,"sub" 表示 subject還有 "exp" 表示expiration time。建立 JWT 時,這些字段很是有用,可是他們是可選的,想了解更多有關 JWT 標準字段的詳細信息,請參閱 JWT 上的維基百科頁面。
請記住,數據的大小會影響 JWT 的總體大小,一般這不是問題,可是,JWT 太大可能會對性能產生負面影響並致使延遲。
簽名的計算方式經過如下的僞代碼進行表述
// signature algorithm
data = base64urlEncode( header ) + 「.」 + base64urlEncode( payload )
hashedData = hash( data, secret )
signature = base64urlEncode( hashedData )
複製代碼
該算法的做用是經過 base64url 對步驟1和步驟2中建立的頭和關鍵信息(payload)進行編碼。而後經過點(.)來鏈接兩個編碼字符串,構成數據 data 。在 JWT 頭部使用指定的散列算法對數據字符串使用密鑰進行散列,並將生成的散列數據分配給 hashedData。而後對該散列數據進行 base64url 編碼以產生 JWT 簽名。
在該例子中,頭部和關鍵信息(payload)都是 base64url 編碼的
// header
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
// payload
eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ
複製代碼
而後,在編碼頭和編碼關鍵負載(payload)中週期的加入攜帶密鑰的應用指定簽名算法,因而,咱們獲得簽名所需的散列數據。在該例子中,這意味着應用HS256算法,並將密鑰設置爲字符串 "secret",在數據字符串上獲取 hashedDate 字符串,以後,經過 base64url 編碼 hashedData 字符串,咱們獲得如下 JWT 簽名
// signature
-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM
複製代碼
咱們已經建立了全部的組件(3個),如今咱們能夠經過它們來建立 JWT了。請記住 JWT 的結構 header.payload.signature ,咱們使用經過 base64url 編碼的 header 和 payload,以及步驟 3 中籤署的簽名,只須要組合這些組件並經過句號(.)分隔它們。
// JWT Token
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ.-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM
複製代碼
經過瀏覽器,在jwt.io上你能夠嘗試建立屬於本身的 JWT。
回到這個例子,如今該認證服務器可以發送 JWT 給用戶了。
要理解使用 JWT 的目的,並而不是經過任何的方式手段去隱藏或者模糊數據。使用 JWT 的緣由是爲了證實發送的數據其實是由真實的源建立的。
如上述步驟所示,JWT 內的數據通過編碼和簽名而不是加密的。編碼數據的目的是轉換數據的結構。一方面簽名數據容許數據接收器驗證數據源的真實性。所以,編碼和簽名數據不會保護數據。另外一方面,加密的主要目的是保護數據並防止未經受權的訪問。有關編碼和加密之間差別的詳細說明,以及有關散列如何工做的更多信息,請參閱此文章
因爲 JWT 僅被簽名和編碼,而且因爲 JWT 未加密,所以 JWT 不保證敏感數據的任何安全性。
在第三個例子中,咱們使用由 HS256 算法簽名的 JWT,其中只有身份驗證服務器和應用服務器知道密鑰。當應用程序設置其身份認證的時候,應用服務器從身份驗證服務器接收密鑰。因爲應用程序知道密鑰,所以當用戶對應用程序調用JWT 鏈接的 API 時,應用程序能夠在 JWT 上執行與步驟 3 相同的簽名算法。而後該應用程序可以驗證自身經過哈希操做得到的簽名與 JWT 自己獲得的簽名是否匹配(即,它與由認證服務器建立的 JWT 簽名匹配)。若是簽名匹配,這意味着 JWT 有效,表示 API 的調用是來自認證服務器的。除此以外,若是簽名不匹配,則表示收到的 JWT 無效,這意味着你的應用程序正受到潛在的攻擊。所以,經過驗證 JWT ,應用程序在自身和用戶之間添加了一層信任。
咱們瞭解了 JWT 是什麼,如何建立和驗證它們,以及如何使用它們來確保應用程序與其用戶之間的信任關係。這是瞭解 JWT 的基礎和起點。在確保應用程序中的信任和安全性難題中,JWT 只是其中之一。
應該注意的是,本文中描述的 JWT 認證設置使用的是對稱密鑰算法(HS256),你也能夠以相似的方式設置 JWT 身份驗證,除非你使用非對稱算法(例如:RS256)這類算法的身份驗證服務器具備密鑰,而且應用程序服務器具備公鑰。查看此 Stack Overflow 問題瞭解對稱和非對稱算法的差別性及其詳細分類。
還應該注意,JWT 應該經過 HTTPS(而不是 HTTP)鏈接。HTTPS 能夠有效的防止未經受權的用戶經過攔截服務器和用戶之間通信的方式來發送 JWT。
此外,若是你的 JWT 關鍵信息(payload)有一部分過時了,那麼整個 JWT 將被視爲無效,不能再使用了。