前言:
什麼是JWT:
Json web token (JWT), 是爲了在網絡應用環境間傳遞聲明而執行的一種基於JSON的開放標準((RFC 7519),
該token被設計爲緊湊且安全的,特別適用於分佈式站點的單點登錄(SSO)場景。
JWT的聲明一般被用來在身份提供者和服務提供者間傳遞被認證的用戶身份信息,以便於從資源服務器獲取資源,也可以增加一些額外的其它業務邏輯所必須的聲明信息,該token也可直接被用於認證,也可被加密。
本篇只介紹了理論知識相關,對應.Net Core的使用見:
https://github.com/Ybbbb/JsonWebToken
JWT的組成
頭部 header
頭部承載兩部分信息:聲明類型和聲明加密的算法 ,
完整的頭部就像下面這樣的JSON:
header: {
'typ': 'JWT', // 類型
'alg': 'HS256' // 使用的加密算法 通常直接使用 HMAC SHA256
}
將頭部進行base64UrlEncode加密(該加密是可以對稱解密的),構成了第一部分
載荷 payload
載荷就是存放有效信息的地方。這個名字像是特指飛機上承載的貨品,
注意:不應該在jwt的payload部分存放敏感信息,因爲該部分是客戶端可解密的部分。
這些有效信息包含三個部分:
1、標準聲明:
- iss(issuer):jwt簽發者
- sub(subject):jwt所面向的用戶
- aud(audience):接收jwt的一方
- exp(expiration):jwt的過期時間
- nbf(notBefore):jwt的使用時間不能早於該時間
- iat(issuedAt): jwt的簽發時間
- jti (jwtId):jwt的唯一身份標識,主要用來作爲一次性token,從而回避重放攻擊。
2、公共的聲明 :
公共的聲明可以添加任何的信息,一般添加用戶的相關信息或其他業務需要的必要信息
3、私有的聲明:
私有聲明是提供者和消費者所共同定義的聲明,一般不建議存放敏感信息,因爲base64是對稱解密的,意味着該部分信息可以歸類爲明文信息。
將playload進行base64UrlEncode加密,得到Jwt的第二部分:
payload:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
簽名哈希 signature
簽名哈希部分是對上面兩部分數據簽名,通過指定的算法生成哈希,以確保數據不會被篡改。
首先,需要指定一個密碼(secret)。該密碼僅僅爲保存在服務器中,並且不能向用戶公開。然後,使用標頭中指定的簽名算法(默認情況下爲HMAC SHA256)根據以下公式生成簽名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
在計算出簽名哈希後,JWT頭,有效載荷和簽名哈希的三個部分組合成一個字符串,每個部分用"."分隔,就構成整個JWT對象。
JWT生成編碼後的樣子:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. // 頭部
eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8y. // 載荷 這裏爲了方便展示縮略了,其實載荷很長
At4MhxKfSZ7JrMGxGtrLXyE6LMxLi1dChftstRU-1LY // 簽名
JWT驗證的過程
詳細過程可參考:https://www.jianshu.com/p/6bfeb86885a3
1、首先客戶端發出請求,並在請求頭裏面附帶上Token。
2、服務端在接受到請求後拿到Token後,通過逗號分隔開頭部、載荷和簽名。並進行解碼,拿到頭部裏聲明的簽名算法。
3、對頭部和載荷用同一算法再次以同樣的方法簽名,並與請求中拿到的簽名進行對比,如果對比之後發現,自己計算出來的簽名和接收到的簽名不一樣,那麼就說明這個Token的內容被別人動過的,我們應該拒絕這次請求,並返回一個錯誤碼。
應用圖示:
一般是在請求頭裏加入Authorization,並加上Bearer標註:
fetch('api/user/1', {
headers: {
'Authorization': 'Bearer ' + token
}
})
服務端會驗證token,如果驗證通過就會返回相應的資源。
對JWT的總結
優點:
- 在payload 載荷中可以包含足夠多的信息比如用戶名等,以便在後續請求中減少查詢數據庫的機率。
- 因爲token本身是無狀態的,所以可以用一套認證代碼來進行pc端、移動端等多端的授權。
- 因爲token更多的是通過header進行傳遞,所以不存在跨域問題。
- JWT使用已經標準化了,資料也很多,學習成本不高
- 因爲json的通用性,所以JWT是可以進行跨語言支持的,像JAVA,NodeJS,PHP等很多語言都可以使用。
缺點:
JWT的最大缺點是服務器不保存會話狀態,所以在使用期間不可能取消令牌或更改令牌的權限。也就是說,一旦JWT簽發,在有效期內將會一直有效。
使用JWT要注意的地方:
- 不應該在JWT的payload部分存放敏感信息,因爲該部分是客戶端可解密的部分。
- 爲了減少盜用和竊取,JWT不建議使用HTTP協議來傳輸代碼,而是使用加密的HTTPS協議進行傳輸。
對比傳統的session認證:
http協議本身是一種無狀態的協議,而這就意味着如果用戶向我們的應用提供了用戶名和密碼來進行用戶認證,那麼下一次請求時,用戶還要再一次進行用戶認證纔行, 因爲我們並不能知道是哪個用戶發出的請求。
所以爲了讓我們的應用能識別是哪個用戶發出的請求,我們只能在服務器存儲一份用戶登錄的信息,這份登錄信息會在響應時傳遞給瀏覽器,告訴其保存爲cookie,以便下次請求時發送給我們的應用,這樣我們的應用就能識別請求來自哪個用戶了,這就是傳統的基於session認證。
但是這種基於session的認證使應用本身很難得到擴展,隨着不同客戶端用戶的增加,獨立的服務器已無法承載更多的用戶,而這時候基於session認證應用的問題就會暴露出來。
基於session認證所顯露的問題:
開銷大: 每個用戶經過我們的應用認證之後,我們的應用都要在服務端做一次記錄,以方便用戶下次請求的鑑別,通常而言session都是保存在內存中,而隨着認證用戶的增多,服務端的開銷會明顯增大。
擴展性弱: 用戶認證之後,服務端做認證記錄,如果認證的記錄被保存在內存中的話,這意味着用戶下次請求還必須要請求在這臺服務器上,這樣才能拿到授權的資源,這樣在分佈式的應用上,相應的限制了負載均衡器的能力。這也意味着限制了應用的擴展能力。
CSRF:因爲是基於cookie來進行用戶識別的, cookie如果被截獲,用戶就會很容易受到跨站請求僞造的攻擊。