jwt原理(json web token)
我們之前是使用session實現登錄,通過實際密碼+鹽組成字符串進行md5存入redis或者數據庫中,輸入的密碼與實際校驗通過,發送給客戶端一個有效時間的token,期間可以實現登錄訪問了
而jwt無需通過redis或者存儲在數據庫中了,直接通過一串字符串就能拿到信息和時間
優勢
'''
解決跨域問題:這種基於Token的訪問策略可以克服cookies的跨域問題。
服務端無狀態可以橫向擴展,Token可完成認證,無需存儲Session。
系統解耦,Token攜帶所有的用戶信息,無需綁定一個特定的認證方案,只需要知道加密的方法和密鑰就可以進行加密解密,有利於解耦。
防止跨站點腳本攻擊,沒有cookie技術,無需考慮跨站請求的安全問題。
'''
劣勢
'''
jwt一旦發送不能撤回發送的信息
不能防止CSRF攻擊等
'''
jwt格式分為三部分組成,header,playload,sign
header就是json格式 {"typ":"JWT","alg":"HS256","exp":1491066992916},typ表示類型為jwt格式,alg表示加密方式為hs256,exp表示時間
playroad是請求體,根據業務自行定義,可以是一個字典一個列表,一個字符串
sign表示簽名的生成
加密原理:
把header和playload分別使用base64url編碼,接着用'.'把兩個編碼后的字符串連接起來,再把這拼接起來的字符串配合密鑰進行HMAC SHA-256算法加密,最后再次base64編碼下,這就拿到了簽名sign. 最后把header和playload和sign用'.'連接起來就生成了整個JWT
解密原理:
后端服務校驗jwtToken是否有權訪問接口服務,進行解密認證,如校驗訪問者的userid,首先用將字符串按.號切分三段字符串,分別得到header和playload和sign。然后將header.playload拼裝用密鑰和HAMC SHA-256算法進行加密然后得到新的字符串和sign進行比對,
如果一樣就代表數據沒有被篡改,然后從頭部取出exp對存活期進行判斷,如果超過了存活期就返回空字符串,如果在存活期內返回userid的值
校驗原理:
整個jwt的結構是由header.playload.sign連接組成,只有sign是用密鑰加密的,而所有的信息都在header和playload中可以直接獲取,sign的作用只是校驗header和playload的信息是否被篡改過,所以jwt不能保護數據,但以上的特性可以很好的應用在權限認證上
itsdangerous是python基於jwt的實現的,我們可以用第三方庫來實現
itsdangerous安裝
pip install itsdangerous
Signer(secret_key, salt=None, sep='.', key_derivation=None, digest_method=None, algorithm=None)
# 簽名加密/解密 from itsdangerous import Signer s = Signer('rainbol') # 設置鹽值 res = s.sign('me').decode() # 設置簽名 print(res) # me.OJZBr7m8IGARJ0qzK07wdy9xAJM 返回一個簽名.字符串 print(s.unsign('me.OJZBr7m8IGARJ0qzK07wdy9xAJM').decode()) # 解簽 返回me ,如果中間任意字符串不正確都會報錯
TimestampSigner(secret_key, salt=None, sep='.', key_derivation=None, digest_method=None, algorithm=None)
# 帶時間簽名加密/解密
from itsdangerous import TimestampSigner s = TimestampSigner('rainbol') # 設置鹽值 string = s.sign('123') # 加簽 print(string.decode()) # 返回123.XNkPLg.Qr0E6jJvj7-tg40SBtC0kunkM1w res = s.unsign(string, max_age=20) # 解簽 max_age設置過期時間20秒 print(res.decode()) # 返回123 ,如果中間任意字符串不正確或者超過max_age時間都會報錯
Serializer(secret_key, salt='itsdangerous', serializer=None, signer=None, signer_kwargs=None)
# 序列化 from itsdangerous import Serializer l = Serializer('rainbol') # 設置鹽值 re = l.dumps(['1314', '2233']) print(re) # ["1314","2233"].V2vY21tK5Nc8wYP6rlY9-F2zhH0 print(l.loads(re)) # ['1314', '2233'] 如果不正確會報錯 # 如果需要加入時間戳,需要加入TimedSerializer,用法和TimestampSigner一致
# url安全序列化 from itsdangerous import URLSafeSerializer l = URLSafeSerializer('rainbol') re = l.dumps(['122', '2233']) # 返回 WyIxMjIiLCIyMjMzIl0.SujNVMlTr3RTkuQIHIRjl1R7tTs 序列化更加安全,其他一致
# jsonweb簽名 from itsdangerous import JSONWebSignatureSerializer s = JSONWebSignatureSerializer('rainbol') # 設置鹽值 res = s.dumps({'rainbol': '123456'}) # 設置加密字典 print(res.decode()) # eyJhbGciOiJIUzUxMiJ9.eyJyYWluYm9sIjoiMTIzNDU2In0.CCMq-9G8HkoMb7NFXHeTSg0iG2lndsyZL-fcrV85gfuXBekNP3AWa3bUTYWWHuiFRsueqZ495S5bMxFCoglxZg res = s.loads('eyJhbGciOiJIUzUxMiJ9.eyJyYWluYm9sIjoiMTIzNDU2In0.CCMq-9G8HkoMb7NFXHe' 'TSg0iG2lndsyZL-fcrV85gfuXBekNP3AWa3bUTYWWHuiFRsueqZ495S5bMxFCoglxZg', return_header=True) # 解密字典 print(res) # 返回{'rainbol': '123456'} 如果中間任意字符串不正確都會報錯
在實際開發過程中用以下方法即可
#帶有時間的jsonweb簽名 import itsdangerous salt = 'saO)(&)H' # 設置鹽值 t = itsdangerous.TimedJSONWebSignatureSerializer(salt, expires_in=600) # 指定參數,第一個是鹽值,第二個是TTL加密過期時間 res = t.dumps({'username': 'rainbol', 'password': '123456'}) # 設置加密的字典 print(res.decode()) # 取加密信息 session = 'eyJhbGciOiJIUzUxMiIsImlhdCI6MTU1NzcyNzM4NywiZXhwIjoxNTU3NzI3OTg3fQ.eyJ1c2VybmFtZSI6InJhaW5ib2wiLCJwYXNzd29yZCI6IjEyMzQ1NiJ9.5r2_YYH06HLCW9Ix7EB5UGpk0wPD8RmWib0EoD9lgZIaRHEaH-qrMfQeKPriQNplD1gNLMM2Dn9NX-zoSz20Gg' res = t.loads(session)#解析加密信息,如果加密信息不正確會報錯 print(res)
參考https://blog.csdn.net/weiker12/article/details/68950279
版權聲明:本文原創發表於 博客園,作者為 RainBol 本文歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則視為侵權。