為什麼要使用JWT認證?構成和原理又是什麼?怎麼還有Base64的事?我都寫了
目錄
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?建議參考下博文
為什麼要使用base64編碼,有哪些情景需求? - 知乎 (zhihu.com)