2 django jwt
1 什么是jwt
JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。
2 session 认证的不足
Session: 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。
3 token 鉴权机制
基于token的鉴权机制类似于http协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于token认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。
流程:
1 用户使用用户名密码来请求服务器
2 服务器进行验证用户的信息
3 服务器通过验证发送给用户一个token
4 客户端存储token,并在每次请求时附送上这个token值
5 服务端验证token值,并返回数据
这个token必须要在每次请求时传递给服务端,它应该保存在请求头里, 另外,服务端要支持CORS(跨来源资源共享)策略,一般我们在服务端这么做就可以了Access-Control-Allow-Origin: *。
4 jwt 组成
JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是: 1 Header 2 Payload 3 Signature 因此,一个典型的JWT看起来是这个样子的:xxxxx.yyyyy.zzzzz
5 jwt 详解
- header(固定)
{ 'typ': 'JWT', // 类型 'alg': 'HS256' // 算法 }
- payload(最主要的是exp-过期时间)
包括三个部分 1 标准中注册的声明 2 公共的声明 3 私有的声明 1 标准中注册的声明 (建议但不强制使用) : iss: jwt签发者 sub: jwt所面向的用户 aud: 接收jwt的一方 exp: jwt的过期时间,这个过期时间必须要大于签发时间 nbf: 定义在什么时间之前,该jwt都是不可用的. iat: jwt的签发时间 jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。 2 公共的声明 : 公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密. 3 私有的声明 : 私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
- Signature(签名)
jwt的第三部分是一个签证信息,这个签证信息由三部分组成: 1 header (base64后的) 2 payload (base64后的) 3 secret
6 自定义 jwt
import base64 import json import time import hmac import copy class Jwt(): @staticmethod def encode_jwt(self_payload, key, exp=300): # self_payload 私有声明字典 # key 自定义 # exp token过期时间,单位s # 初始化 header,固定 header = {'typ': 'JWT', 'alg': 'HS256'} # separators 使用没有空格的逗号和冒号 # sort_key 按字符排序 header_json = json.dumps(header, separators=(',',':'), sort_keys=True) # header_json.encode() 转字节串 header_bs = Jwt.b64encode(header_json.encode()) # 初始化 payload # copy.deepcopy(self_payload) 拷贝一份传入的字典,避免数据被污染 payload = copy.deepcopy(self_payload) payload['exp'] = time.time() + exp payload_json = json.dumps(payload, separators=(',',':'), sort_keys=True) payload_bs = Jwt.b64encode(payload_json.encode()) # 初始化 sign hm = hmac.new(key.encode(), header_bs + b'.' + payload_bs, digestmod='SHA256') hm_bs = Jwt.b64encode(hm.digest()) return header_bs + b'.' + payload_bs + b'.' + hm_bs # 以下 token 需要优化内容: # 1 使用 base64url 生成 # 2 去掉 token 中的等号,校验时在加入 # eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTk3NTQzNDMuNTU0OTg0OCwidXNlcm5hbWUiOiJsaXppIn0=.q48gPBu3Ug3Azet6pAh1zlZsvL+ClgiUd9HzMHOFAIU= # eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MTk3NjAwNzcuNDg4MjAxLCJ1c2VybmFtZSI6ImxpemkifQ.SeKAL0ngnNn9FQHvtRxO7FsiM8MHJrHtBXDR5G50BHc @staticmethod def b64encode(json_str): return base64.urlsafe_b64encode(json_str).replace(b'=',b'') @staticmethod def b64decode(b_str): # b64串一定是4的倍数 rem = len(b_str) % 4 if rem > 0: b_str += b'=' * (4-rem) return base64.urlsafe_b64decode(b_str) @staticmethod def decode_jwt(token, key): # 校验签名 header_bs, payload_bs, sign_bs = token.split(b'.') hm = hmac.new(key.encode(), header_bs + b'.' + payload_bs, digestmod='SHA256') if sign_bs != Jwt.b64encode(hm.digest()): raise # 校验时间 payload_js = Jwt.b64decode(payload_bs) payload = json.loads(payload_js) now = time.time() if int(now) > int(payload['exp']): raise # 返回 payload 明文部分 return payload if __name__=='__main__': t = Jwt.encode_jwt({'username':'lizi'}, '123456', 300) print(Jwt.decode_jwt(t, '123456'))