在本文中,將解釋JSON Web Tokens(JWT)的基本原理以及使用原因。 JWT 是確保應用程式信任和安全的重要部分。 JWT 允許以安全的方式表示諸如使用者資料之類的宣告。
為了解釋 JWT 如何工作,讓我們從一個抽象的定義開始。
JSON Web令牌(JWT)是一種 JSON 物件,在 RFC 7519 中定義為在兩方之間表示一組資訊的安全方式。 JWT 由頭部(header),負載(payload)和簽名(signature)組成。
簡單地說,JWT只是一個具有以下格式的字串:
header.payload.signature
複製程式碼
應該注意,雙引號字串被認為是有效的 JSON 物件。
為了說明實際使用 JWT 的方式和原因,我們將使用一個簡單的示例(參見下圖)。 此示例中的三個不同的實體是使用者,應用程式伺服器和身份驗證伺服器。 驗證伺服器將向使用者提供 JWT。 使用 JWT,使用者可以安全地與應用程式通訊。
應用程式如何使用JWT驗證使用者的真實性
在該示例中,使用者首先使用認證伺服器的登入系統登入認證伺服器(例如,使用者名稱和密碼,Facebook登入,Google登入等)。 然後,身份驗證伺服器建立 JWT並將其傳送給使用者。 當使用者對應用程式進行 API 呼叫時,使用者將傳遞 JWT 以及 API 呼叫。 在這個例項中,應用程式伺服器將可以驗證傳入的 JWT 是否是由身份驗證伺服器建立的(驗證過程將在稍後更詳細地說明)。當使用者使用附加的 JWT 進行API 呼叫時,應用程式可以使用 JWT 來驗證該 API 呼叫是否來自經過身份驗證的使用者。
現在,將更深入地研究 JWT 本身及其構建和驗證的方式。
Step 1. 建立 Header
JWT 的 Header 部分包含有關如何計算 JWT 簽名的資訊,是一個以下形式的 JSON 物件:
{
"typ": "JWT",
"alg": "HS256"
}
複製程式碼
在上面的 JSON 中,“typ”鍵的值指定物件是JWT,“alg”鍵的值指定用於建立 JWT 簽名的演算法。 在示例中,我們使用 HMAC-SHA256演算法(一種使用金鑰的雜湊演算法)來計算簽名(在步驟3中會更詳細的介紹)。
Step 2. 建立 PAYLOAD
JWT 的 payload 部分時是儲存在 JWT 內的資料。在我們的示例中,身份驗證伺服器建立一個JWT,其中儲存有使用者資訊,特別是使用者ID。
{
"userId": "b08f86af-35da-48f2-8fab-cef3904660bd"
}
複製程式碼
The data inside the payload is referred to as the “claims” of the token.
在我們的示例中,我們只將一個宣告放入 payload 中。 你可以根據需要新增任意數量的宣告。JWT 規定了7個官方欄位,供選用。
iss (issuer):簽發人
exp (expiration time):過期時間
sub (subject):主題
aud (audience):受眾
nbf (Not Before):生效時間
iat (Issued At):簽發時間
jti (JWT ID):編號
複製程式碼
除了官方欄位,你還可以在這個部分定義私有欄位。請記住,資料的大小將影響JWT的總體大小,這通常不是問題,但過大的 JWT 可能會對效能產生負面影響並導致延遲。
Step 3. 建立 SIGNATURE
簽名使用以下虛擬碼計算:
// signature algorithm
data = base64urlEncode( header ) + “.” + base64urlEncode( payload )
hashedData = hash( data, secret )
signature = base64urlEncode( hashedData )
複製程式碼
該演算法所做的是 base64url 對在步驟1和2中建立的header和payload進行編碼。然後,演算法將得到的編碼字串用“點”(.)連在一起。
在示例中,header 和 payload 被 base64url 編碼為:
// header
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
// payload
eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ
複製程式碼
然後,在加入週期的編碼頭和編碼有效載荷上應用帶有金鑰的指定簽名演算法,我們得到簽名所需的雜湊資料。 在我們的例子中,這意味著在資料字串上應用 HS256 演算法,並將金鑰設定為字串“secret”,以獲取 hashedData字串。 之後,通過base64url 編碼 hashedData 字串,我們得到以下JWT簽名:
// signature
-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM
複製程式碼
Step 4. 把 JWT 的三個部分組合在一起
現在我們已經建立了所有三個元件,我們可以建立JWT。 記住JWT的header.payload.signature結構,我們只需要組合以上的三個部分,用點(.)分隔它們。
// JWT Token
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJiMDhmODZhZi0zNWRhLTQ4ZjItOGZhYi1jZWYzOTA0NjYwYmQifQ.-xN_h82PHVTCMA9vdoHrcZxH-x5mb11y1537t3rGzcM
複製程式碼
你可以嘗試通過 jwt.io 建立自己的JWT。
回到我們的示例,身份驗證伺服器現在可以將此 JWT 傳送給使用者。
JWT 如何保護我們的資料?
要理解使用 JWT 的目的不是以任何方式隱藏或模糊資料,而是為了證明傳送的資料是由真實的來源建立的。
如前面的步驟所示,JWT 內的資料是經過編碼和簽名的,而不是加密的。 編碼資料的目的是轉換資料的結構。 簽名資料允許資料接收器驗證資料來源的真實性。 因此,編碼和簽名資料不會保護資料。 另一方面,加密的主要目的是保護資料並防止未經授權的訪問。 有關編碼和加密之間差異的更詳細說明,請參閱此文章。
由於 JWT 僅被簽名和編碼,並且由於 JWT 未加密,因此 JWT 不能保證敏感資料的安全性。
Step 5. 校驗 JWT
在我們的示例中,我們使用的是由 HS256 演算法簽名的JWT,其中只有身份驗證伺服器和應用伺服器知道金鑰。當應用程式設定其身份驗證過程時,應用程式伺服器從身份驗證伺服器接收金鑰。由於應用程式知道金鑰,當使用者對應用程式進行帶有 JWT 附加的 API 呼叫時,應用程式可以執行與 JWT 上的步驟3相同的簽名演算法。然後,應用程式可以驗證從其自己的雜湊操作獲得的簽名是否與 JWT 本身上的簽名匹配(即,它與由認證伺服器建立的 JWT 簽名匹配)。如果簽名匹配,則表示 JWT 有效,表示 API 呼叫來自可信源。否則,如果簽名不匹配,則表示收到的 JWT 無效,這可能是對應用程式的潛在攻擊的指示。因此,通過驗證 JWT,應用程式在其自身和使用者之間新增了一層信任。
結論
我們瞭解了 JWT 是什麼,如何建立和驗證它們,以及如何使用它們來確保應用程式與其使用者之間的信任。這是瞭解 JWT 基礎知識及其有用之處的起點。 JWT 只是確保應用程式中的信任和安全性的難題之一。 應該注意,本文中描述的 JWT 身份驗證設定使用對稱金鑰演算法(HS256)。你也可以以類似的方式設定 JWT 身份驗證,除非使用非對稱演算法(例如RS256),其中身份驗證伺服器具有金鑰,並且應用程式伺服器具有公鑰。檢視此 Stack Overflow問題,瞭解使用對稱和非對稱演算法之間差異的詳細分類。 還應該注意,JWT 應該通過 HTTPS 連線傳送。擁有 HTTPS 有助於防止未經授權的使用者通過使用它來竊取所傳送的 JWT,從而無法攔截伺服器和使用者之間的通訊。 此外,在 JWT 中設定較短的過期時間十分重要,這樣如果舊的 JWT 受到盜用,它們將被視為無效並且不能再使用。
《IVWEB 技術週刊》 震撼上線了,關注公眾號:IVWEB社群,每週定時推送優質文章。