JWT (json web token)是一種http request主流加密方式,相比傳統的token,JWT的優勢在於無狀態傳播,不需要存儲到數據庫,就可以實現安全令牌驗證,缺點是一經簽發,exp失效前令牌都會一直有效,此認證狀態無法更改。
JWT無法被破解的關鍵,是在其第三部分簽名裏的哈希加密中的鹽值。
由於哈希加密是無法直接解密的,所以jwt實際上只是重複使用加密的過程進行驗證而已。
具體過程
一、頭部 (head)
包含了具體的算法,如HS256 HS512等,typ類型:JWT,通常寫法
二、負載(payload)
包含了關鍵信息,包含用戶名,過期日期,其他自定義鍵值等
三、簽名(sign)
將一二部分進行base64加密後的結果再進行哈希加鹽加密,處理後的最後字符串爲關鍵驗證信息。
注意:base64加密方式中原表的“/“,"+","="這三個字符,在進行JWT加密時通用的base64url加密,在加密過程中將其會替換成“-”,“_”,並去掉“=”。
具體加密過程:
將頭部字典變爲json格式(json.dumps),並將其分割符號變爲“,”與“:”,去掉空格緊湊分割
將頭部進行base64url加密,再去掉裏面的“=”,
將負載中的exp添加鍵值:即爲過期日期,格式爲int
將其同樣處理爲json格式,再進行base64url加密
將頭部和負載相連,中間用"."符號連接。
檢查鹽值,如果鹽裏面有字符串,將其encode爲二進制字節串
對連接好的頭部負載進行哈希加密,輸入鹽值,方法(hmac.new(鹽值,簽名,digestmod='SHA256'))
將處理好的sign再進行簽名化處理 (x.digest()方法)
爲其再次進行base64加密處理,得到最終的簽名令牌
最後,將頭部、負載、簽名再使用b“.”連接起來,就得到完全的JWT了,這就是其完整處理過程
代碼:
import copy
import hmac
import json
import base64
import time
class Jwt():
def __init__(self):
pass
@staticmethod
def encode(payload, key, exp=300):
#create header
header = {'alg':'HS256','typ':'JWT'}
header_j = json.dumps(header, separators=(',',':'),sort_keys=True)
header_bs = Jwt.b64encode(
header_j.encode())
payload = copy.deepcopy(payload)
payload['exp'] = int(time.time()) + exp
payload_j = json.dumps(payload, separators=(',',':'), sort_keys=True)
payload_bs = Jwt.b64encode(payload_j.encode())
to_sign_str = header_bs + b'.' + payload_bs
if isinstance(key, str):
key = key.encode()
hmac_obj = hmac.new(key,to_sign_str,
digestmod='SHA256')
sign = hmac_obj.digest()
sign_bs = Jwt.b64encode(sign)
return header_bs + b'.' + payload_bs + b'.' + sign_bs
@staticmethod
def b64encode(s):
return base64.urlsafe_b64encode(s).replace(b'=',b'')
@staticmethod
def b64decode(s):
mod = len(s) % 4
s += b'='*(4-mod)
return base64.urlsafe_b64decode(s)
@staticmethod
def decode(token,key):
header_bs,payload_bs,sign = token.split(b'.')
if isinstance(key, str):
key = key.encode()
hm = hmac.new(key, header_bs + b'.' + payload_bs,digestmod='SHA256')
new_sign = Jwt.b64encode(hm.digest())
if sign != new_sign:
raise JwtSignError('Your token is not true')
payload_j = Jwt.b64decode(payload_bs)
print(type(payload_j))
payload = json.loads(payload_j)
exp = payload['exp']
now = time.time()
if now > exp:
raise JwtSignError('Your token is valid')
return payload
class JwtSignError(Exception):
def __init__(self,error_msg):
self.error = error_msg
def __str__(self):
return '<JwtError error %s>' %(self.error)
if __name__ == "__main__":
res = Jwt.encode({'username':'lxxx'},'abcdef1234',1)
print(Jwt.decode(res,'abcdef1234'))