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

 

posted @ 2021-05-04 18:20  栗子测试开发  阅读(91)  评论(0)    收藏  举报