本文首發於 hzzly的博客javascript
原文連接:JWT--JSON WEB TOKENhtml
JSON Web Token (JWT), 是爲了在網絡應用環境間傳遞聲明而執行的一種基於JSON的開放標準(RFC 7519),該token被設計爲緊湊且安全的,特別適用於分佈式站點的單點登陸(SSO)場景。JWT的聲明通常被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便於從資源服務器獲取資源,也能夠增長一些額外的其它業務邏輯所必須的聲明信息,該token也可直接被用於認證,也可被加密。java
JWT是目前最流行的跨域認證解決方案之一。redis
在看爲何使用以前,咱們必需要先了解以前咱們是如何進行驗證請求的。算法
在 Session 認證方式中,用戶登陸後發給服務器,服務器在接收並驗證發送過來的帳號密碼請求以後,就會把這個用戶信息放入 Session 中,而後把 Session 存在服務器上,這樣服務器就知道了這個用戶的存在,當下一次用戶訪問的時候,就能認證了。api
但由於咱們知道http協議是一種無狀態的協議,也就是說當下一次用戶發送請求的時候,請求中沒有任何信息能代表用戶身份!也就是說不知道請求是誰發出來了,這樣也就不能認證了。跨域
因此就須要利用 Cookie 來管理 Session,即把 SessionID 放入 HTTP 響應中發給客戶端,並保存在客戶端,當客戶端發送下一次請求的時候,就把這個 SessionID 一塊兒發送回來,這樣就能此次的請求是誰發出來的了。瀏覽器
擴展:Cookie 是由客戶端(一般是瀏覽器)保存的小型文本信息,其內容是一系列的鍵值對,是由 HTTP 服務器設置並保存在瀏覽器上的信息。緩存
內存開銷大
: 咱們知道 Session 是存在服務器上的,實際上爲了加快認證的速度,咱們通常都會放在內存中,這樣當用戶基數大的時候,內存的開銷就會很大。固然也能夠將 Session 存入到 Session 表或者是緩存(redis等)中,可是依舊會有這樣的問題。安全
安全性(CSRF)
: 由於是基於 Cookie 進行用戶識別,若是 Cookie 被截獲,用戶就會很容易收到跨站請求僞造的攻擊。
分佈式負載均衡
: 由於 Session 信息是被單個服務器所保存的,因此在分佈式系統中就不能適用了。好比 Session 一開始是保存在 A 服務器上,可是下一次請求的時候,這個請求被服務器負載均衡轉發到了 B 服務器,而 B 服務器則沒有這個 Session 信息,因此就不能用過認證了。
由於 JWT 是由服務端生成的,經過請求傳給客戶端(客戶端能夠以任意方式存放)。因此服務器不須要存儲任何 JWT 信息。這樣就能避免了上述 Session 的幾個問題了。固然 JWT 還有其自身的一些優勢。
輕量級
:JWT是很是輕量級的,傳輸的方式多樣化,能夠經過URL/POST參數/HTTP頭部等方式傳輸。
無狀態/跨域認證
:token包含全部用於標識用戶的信息,這消除了對會話狀態的須要。若是咱們使用負載均衡,咱們依然能夠將token傳遞給任何服務器,而不是存儲在咱們登陸的同一臺服務器上。
安全性
:無需擔憂跨站請求僞造(CSRF)攻擊。
由三個部分組成:header.payload.signature
header
:包含了兩個部分 typ 和 alg,分別是聲明類型和JWT的加密算法。
{
"alg": "HS256",
"typ": "JWT"
}
複製代碼
通過 base64URL 加密以後獲得 JWT 的第一部分信息:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
複製代碼
payload
:負載,存放有效信息的地方。這些有效信息包含三個部分:標準中註冊的聲明、公共的聲明 和 私有的聲明。
除了官方字段,你還能夠在這個部分定義私有字段:
{
"sub": "1234567890",
"nickname": "hzzly",
"username": "hzzly",
"scopes": [ "admin", "user" ]
}
複製代碼
通過 base64URL 加密以後獲得 JWT 的第二部分信息:
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmlja25hbWUiOiJoenpseSIsInVzZXJuYW1lIjoiaHp6bHkiLCJzY29wZXMiOlsiYWRtaW4iLCJ1c2VyIl19
複製代碼
signature
:是對前兩部分的簽名,防止數據篡改。由三個部分組成:header、payload 和 secret。其中 header 和 payload 都是加密後的字符串,secret就是一個字符串(密鑰)。
const signature = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret);
複製代碼
算出簽名之後,把 header、payload、signature 三個部分拼成一個字符串,每一個部分之間用"點"(.)分隔,就能夠返回給客戶端。
最終的jwt:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmlja25hbWUiOiJoenpseSIsInVzZXJuYW1lIjoiaHp6bHkiLCJzY29wZXMiOlsiYWRtaW4iLCJ1c2VyIl19.sXaHGg9SWyRpl-rhiSBFuD01G4yE3Gmi5m-JD7u6YyI
複製代碼
面提到,header 和 payload 串型化的算法是 base64URL。這個算法跟 base64 算法基本相似,但有一些小的不一樣。
JWT 做爲一個令牌(token),有些場合可能會放到 URL(好比 api.example.com/?token=xxx)。base64 有三個字符+、/和=,在 URL 裏面有特殊含義,因此要被替換掉:=被省略、+替換成-,/替換成_ 。這就是 Base64URL 算法。
客戶端收到服務器返回的 JWT,能夠儲存在 sessionStorage 或 localStorage 裏面。
此後,客戶端每次與服務器通訊,都要帶上這個 JWT。須要把它放在 HTTP 請求的頭信息 Authorization 字段裏面。
Authorization: Bearer <token>
複製代碼
另外一種作法是,跨域的時候,JWT 就放在 POST 請求的數據體裏面。