DRF JWT認證(一)
JWT認證
Json web token (JWT), 是為了在網路應用環境間傳遞宣告而執行的一種基於JSON的開放標準((RFC 7519).該token被設計為緊湊且安全的,特別適用於分散式站點的單點登入(SSO)場景。JWT的宣告一般被用來在身份提供者和服務提供者間傳遞被認證的使用者身份資訊,以便於從資源伺服器獲取資源,也可以增加一些額外的其它業務邏輯所必須的宣告資訊,該token也可直接被用於認證,也可被加密。
Json web token (JWT),token是一種認證機制,用在web開發方向,叫JWT
為什麼使用JWT 認證?
我們想記錄使用者註冊或者登入後的狀態,或者建立身份認證的憑證,可以使用Session認證機制,或者我們現在所要說的JWT 認證機制,那麼為什麼有了Session認證機制還要使用JWT??
Session機制
JWT機制
圖解後,可知如果登入使用者很多,需要在後端存很多資料,頻繁查詢資料庫,導致效率低,JWT就可以使我們可以不在服務端存資料,又夠保證資料安全,在客戶端存資料 ----> token認證機制
下面我們瞭解一下JWT的構成和工作原理
構成和工作原理
JWT的構成
JWT由3部分組成:標頭(Header)、有效載荷(Payload)和簽名(Signature)。在傳輸的時候,會將JWT的3部分分別進行Base64編碼後用.
進行連線形成最終傳輸的字串。比如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
1. header
JWT頭是一個描述JWT後設資料的JSON物件,alg屬性表示簽名使用的演算法,預設為HMAC SHA256(寫為HS256);typ屬性表示令牌的型別,JWT令牌統一寫為JWT。最後,使用Base64 URL演算法將上述JSON物件轉換為字串儲存
第一段頭部承載的資訊:
- 宣告型別,這裡是jwt
- 宣告加密的演算法 通常直接使用 HMAC SHA256
完整的頭部就像下面這樣的JSON:
{
"typ": "JWT",
"alg": "HS256"
}
然後將頭部進行base64加密(該加密是可以對稱解密的),構成了第一部分
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
2. payload
第二段是載荷,載荷就是存放有效資訊的地方,是JWT的主體內容部分,也是一個JSON物件,承載的資訊:
- 標準中註冊的宣告
- 公共的宣告
- 私有的宣告
標準中註冊的宣告 (建議但不強制使用) :
- iss: jwt簽發者
- sub: jwt所面向的使用者
- aud: 接收jwt的一方
- exp: jwt的過期時間,這個過期時間必須要大於簽發時間
- nbf: 定義在什麼時間之前,該jwt都是不可用的.
- iat: jwt的簽發時間
- jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避時序攻擊。
公共的宣告 : 公共的宣告可以新增任何的資訊,一般新增使用者的相關資訊或其他業務需要的必要資訊,但不建議新增敏感資訊,因為該部分在客戶端可解密.
私有的宣告 : 私有宣告是提供者和消費者所共同定義的宣告,一般不建議存放敏感資訊,因為base64是對稱解密的,意味著該部分資訊可以歸類為明文資訊。
除以上標準註冊宣告欄位外,我們還可以自定義欄位,一般會把包含使用者資訊的資料放到payload中,如下例:
{
"sub": "1234567890",
"name": "HammerZe",
"admin": true
}
注意:雖然說使用者資訊資料可以存放到payload中,但是預設情況下JWT是未加密的,Base64演算法也只是編碼並不會提供安全的加密演算法,一般程式設計師拿到Base64編碼的字串都可以解碼出內容,所以不要存隱私資訊,比如密碼,防止洩露,存一些非敏感資訊
3. signature
簽名雜湊部分是對上面兩部分資料簽名,需要使用base64編碼後的header和payload資料,通過指定的演算法生成雜湊,以確保資料不會被篡改。首先,需要指定一個金鑰(secret)。該密碼僅僅為儲存在伺服器中,並且不能向使用者公開。然後,使用header中指定的簽名演算法(預設情況下為HMAC SHA256)根據以下公式生成簽名
HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret)
簡單的說第三段是簽證資訊,這個簽證資訊由三部分組成:
- header (base64後的)
- payload (base64後的)
- secret
這個部分需要base64加密後的header和base64加密後的payload使用.
連線組成的字串,然後通過header中宣告的加密方式進行加鹽secret
組合加密,然後就構成了JWT的第三部分。
注意:secret是儲存在伺服器端的,jwt的簽發生成也是在伺服器端的,secret就是用來進行jwt的簽發和jwt的驗證,所以,它就是你服務端的私鑰,在任何場景都不應該流露出去。一旦客戶端得知這個secret, 那就意味著客戶端是可以自我簽發jwt了。
關於簽發和核驗JWT,我們可以使用Django REST framework JWT擴充套件來完成。
總結:
注意JWT每部分的作用,在服務端接收到客戶端傳送過來的JWT token之後:
header和payload可以直接利用base64解碼出原文,從header中獲取雜湊簽名的演算法,從payload中獲取有效資料
signature由於使用了不可逆的加密演算法,無法解碼出原文,它的作用是校驗token有沒有被篡改。服務端獲取header中的加密演算法之後,利用該演算法加上secretKey對header、payload進行加密,比對加密後的資料和客戶端傳送過來的是否一致。注意secretKey只能儲存在服務端,而且對於不同的加密演算法其含義有所不同,一般對於MD5型別的摘要加密演算法,secretKey實際上代表的是鹽值
本質原理
JWT認證演算法:簽發與校驗
"""
1)jwt分三段式:頭.體.簽名 (head.payload.sgin)
2)頭和體是可逆加密,讓伺服器可以反解出user物件;簽名是不可逆加密,保證整個token的安全性的
3)頭體簽名三部分,都是採用json格式的字串,進行加密,可逆加密一般採用base64演算法,不可逆加密一般採用hash(md5)演算法
4)頭中的內容是基本資訊:公司資訊、專案組資訊、token採用的加密方式資訊
{
"company": "公司資訊",
...
}
5)體中的內容是關鍵資訊:使用者主鍵、使用者名稱、簽發時客戶端資訊(裝置號、地址)、過期時間
{
"user_id": 1,
...
}
6)簽名中的內容時安全資訊:頭的加密結果 + 體的加密結果 + 伺服器不對外公開的安全碼 進行md5加密
{
"head": "頭的加密字串",
"payload": "體的加密字串",
"secret_key": "安全碼"
}
"""
簽發:根據登入請求提交來的 賬號 + 密碼 + 裝置資訊 簽發 token
"""
1)用基本資訊儲存json字典,採用base64演算法加密得到 頭字串
2)用關鍵資訊儲存json字典,採用base64演算法加密得到 體字串
3)用頭、體加密字串再加安全碼資訊儲存json字典,採用hash md5演算法加密得到 簽名字串
賬號密碼就能根據User表得到user物件,形成的三段字串用 . 拼接成token返回給前臺
"""
校驗:根據客戶端帶token的請求 反解出 user 物件
"""
1)將token按 . 拆分為三段字串,第一段 頭加密字串 一般不需要做任何處理
2)第二段 體加密字串,要反解出使用者主鍵,通過主鍵從User表中就能得到登入使用者,過期時間和裝置資訊都是安全資訊,確保token沒過期,且時同一裝置來的
3)再用 第一段 + 第二段 + 伺服器安全碼 不可逆md5加密,與第三段 簽名字串 進行碰撞校驗,通過後才能代表第二段校驗得到的user物件就是合法的登入使用者
"""
JWT的種類
其實JWT(JSON Web Token)指的是一種規範,這種規範允許我們使用JWT在兩個組織之間傳遞安全可靠的資訊,JWT的具體實現可以分為以下幾種:
nonsecure JWT
:未經過簽名,不安全的JWTJWS
:經過簽名的JWTJWE
:payload
部分經過加密的JWT
nonsecure JWT
未經過簽名,不安全的JWT。其header
部分沒有指定簽名演算法
{
"alg": "none",
"typ": "JWT"
}
並且也沒有Signature
部分
JWS
JWS ,也就是JWT Signature,其結構就是在之前nonsecure JWT的基礎上,在頭部宣告簽名演算法,並在最後新增上簽名。建立簽名,是保證jwt不能被他人隨意篡改。我們通常使用的JWT一般都是JWS
為了完成簽名,除了用到header資訊和payload資訊外,還需要演算法的金鑰,也就是secretKey
。加密的演算法一般有2類:
- 對稱加密:
secretKey
指加密金鑰,可以生成簽名與驗籤 - 非對稱加密:
secretKey
指私鑰,只用來生成簽名,不能用來驗籤(驗籤用的是公鑰)
JWT的金鑰或者金鑰對,一般統一稱為JSON Web Key,也就是JWK
到目前為止,jwt的簽名演算法有三種:
- HMAC【雜湊訊息驗證碼(對稱)】:HS256/HS384/HS512
- RSASSA【RSA簽名演算法(非對稱)】(RS256/RS384/RS512)
- ECDSA【橢圓曲線資料簽名演算法(非對稱)】(ES256/ES384/ES512)
base64編碼與解碼
import base64
import json
dic_info={
"sub": "1234567890",
"name": "HammerZe",
"admin": True
}
'''base64編碼'''
# 必須是bytes型別
s = json.dumps(dic_info).encode('utf8')
enbase64_str = base64.b64encode(s)
print(enbase64_str)
# b'eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogImxxeiIsICJhZG1pbiI6IHRydWV9'
'''base64解碼'''
en_res = b'eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJuYW1lIjogImxxeiIsICJhZG1pbiI6IHRydWV9'
debase64_str = base64.b64decode(en_res).decode('utf8')
print(base64.b64decode(en_res),type(base64.b64decode(en_res)))
# b'{"sub": "1234567890", "name": "HammerZe", "admin": true}' <class 'bytes'>
print(debase64_str)
# {"sub": "1234567890", "name": "HammerZe", "admin": true}
注意
- base64長度必須是4的倍速,如果不夠就使用
=
補齊,
哪些情景需求base64?建議參考下博文