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'))