json web token:用于跨域时代替cookies&session技术,解决http无状态问题,保持会话状态
存储位置为ctrl+shift+i下的存储-本地存储中
实现步骤: 三大组成
header: 明确了算法和token种类,格式为字典,{‘alg’:‘HS256’,‘typ’:‘JWT’},alg代表要使用的算法,typ表名该token的类别,最后需转成json串并用base64加密
payload: 格式为字典:分为公有声明和私有申明
公共声明:JWT提供了内置关键字用于描述常见的问题,用户可根据自己需求添加key,{‘exp’:xxx #token的过期时间,‘iss’:xxx}, #指明此token的签发者(中大厂)
私有申明:用户根据自己业务需求添加的字典
username uid等,e.g. {‘username’:'yutaiixn“},最后需转成json串并用base64加密
signature签名: 用自定义(公司)的key, 对base64后的header + ‘.’ + base64后的payload进行hmac(封装了加盐后进行hash算法加密的库)计算:HS256(自定义的key , base64后的header + ‘.’ + base64后的payload)
jwt结果格式: base(header) + b’.’ +base(payload) +b’.’ + base64(sign)
import json
import base64
import copy
import hmac
class Jwt():
def __init__(self):
pass
#自定义base64加密方法
#信息转成base64通过querystring的形式在url上传递,'+'和'/'在url中不能出现,urlsafe_b64encode()作用同b64encode(),会将+替换成-,/替换成_
#base64加密时会自动将空的部分用‘=’代替,传递时将‘=’省略(replace()),传回时decode的时候再加上
@staticmethod
def b64encode(j_s):
return base64.urlsafe_b64encode(j_s).replace(b'=',b'')
#自定义base64解密方法
def b64decode(b_s):
#补全签发时替换掉的等号,找规律:肯定能被4整除,替换掉的‘=’个数是:4 - 总长 % 4
rem = len(b_s) % 4
if rem > 0:
b_s += b'=' * (4-rem)
return base64.urlsafe_b64decode(b_s)
@staticmethod
def encode(payload,key,exp=300): #假设payload传过来的就是个字典, {'username':'Terry'}
#header
header = {'typ': 'JWT', 'alg': 'HS256'}
#将header转换成json串,由于字典是无序的,需要用sort_keys保证最后字符串的输出顺序是有序的。
#并用seperator保证json串比较瘦,多余的空格一律不要,seperator第一个''为键值对间分隔符,第二个''为key和值间分隔符
header_json = json.dumps(header,sort_keys=True,seperator=(',',':'))
#用base64加密,json串是字符串,base64的参数需要是字节串
header_bs = Jwt.b64encode(header_json.encode())
#payload
#将用户传递来的参数payload深拷贝出来一份,不污染原先的参数payload
my_payload = copy.deepcopy(payload)
#添加token过期时间
my_payload['exp'] = time.time() + int(exp)
#生成json串
payload_json = json.dumps(my_payload,sort_keys=True,separators=(',',':'))
#用base64加密
payload_bs = Jwt.b64encode(payload_json.encode())
#sign
#key和payload一样是用户传进来,并确定用户传进来的key是字符串
if isinstance(key,str):
key = key.encode()
#用自定义的key(该key保证了jwt非常可靠), 对base64后的header + '.' + base64后的payload进行hmac计算
hm = hmac.new(key,header_bs + b'.' + payload_bs,digestmod='SHA256')
#将sign用base64加密,需先根据需求转为二进制(digest())或十六进制(hexdigest())
hm_bs = Jwt.b64encode(hm.digest())
#Jwt结果格式:base(header) + '.' +base(payload) +'.' + base64(sign)
return header_bs + b'.' + payload_bs +b'.' + hm_bs
@staticmethod
def decode(jwt_s,key):
#校验token
#1.检查签名:签名是用前两项拼出来的【取出前两项base64串(结果中每一项有'.'隔开),再做一次hmac签名,与第三部分进行比较,若两者相等,校验成功,失败raise】
#2. 检查时间戳是否过期【过期raise】
#3.return payload明文,即payload字典对象
header_bs,payload_bs,sign_bs = jwt_s.split(b'.')
if isinstance(key,str):
key = key.encode()
hm = hmac.new(key,header_bs + b'.' + payload_bs,digestmod='SHA256')
new_sign_bs = Jwt.b64encode(hm.digest())
if new_sign_bs != sign_bs:
raise
#检查payload中的时间
payload_json = Jwt.b64decode(payload_bs)
#json字符串 -> python对象
payload = json.loads(payload_json)
exp = payload['exp']
now_t = time.time()
if now_t > exp:
#过期
raise
return payload
if __name__ == '__main__':
#生成门票
s = Jwt.encode({'username':'Terry'},'123456',100)
#将门票加个key,做校验,然后打印出结果
d = Jwt.decode(s,'123456') #结果格式{'exp':xxx,'username':'Terry'}
print(s)
print(d)