Python JWT的整体实现过程(详解)

JWT 全称: json-web-token

JWT的大白话解释:
现在比较火的token中的一种,为了解决HTTP协议无状态的问题,开发出来的。就是一种解决方案。

1. 三大组成

  1. header

    在Python来看就是一个字典格式,元数据如下:

    {'alg':'HS256', 'typ':'JWT'}
    # alg代表要使用的 算法 HMAC-SHA256 简写HS256
    # typ表明该token的类别 此处必须为 大写的 JWT
    

    该部分数据需要转换成json串并用base64转码

  2. payload

    在cookie和session中会将用户id或名字写入到其中,在token中会将其写在payload中。

    格式为字典-此部分分为公有声明私有声明

    公有声明: JWT提供了内置关键字用于描述常见的问题

    此部分均为可选项,用户根据自己需求 按需添加key,常见公共声明如下:

    {'exp':xxx,  # EXpiration Time 此token的过期时间的时间戳 time.time()+300s  给一个未来过期时间
     'iss':xxx,  # (issuer) Claim 指明此token的签发者  是那台机器签发的token (当前项目没用)
     'aud':xxx,  # (Audience) Claim 指明此token的签发群体 token签发面向群体是那些人 区分pc,ios,android  (当前项目没用)
     'iat':xxx,  # (ISSued At) Claim 指明此创建时间的时间戳
     # 以上四项是我们的公有声明 保留字
     # 下边私有声明
     'username':'xxx',
    }
    

    私有声明: 用户可根据自己业务需求,添加自定义的key,

    公有声明和私有声明均在同一个字典中;转成json串并用bsase64转码

  3. Signature 签名

    签名规则如下:

    根据header中的alg确定具体算法,以下用HS256为例:

    HS256(自定义的key,base64后的header + b’.‘ + base64后的payload,digestmod=‘SHA256’)

    解释:用自定义的key,对base64后的header + b’.’ + base64后的payload进行hmac计算。

2. jwt结果格式

base64(header) + b'.' + base64(payload) + b'.' + base64(Signature)

注意:

​ 这里base转码需要使用base64.urlsafe_b64encode(b"key")

​ 还需要将base转码后的“=”删除,浪费带宽

最终结果: 是一个字节码

​	b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDU

​	wODI5NTQuMTI0NDUxMiwibmFtZSI6ImxkeXQifQ==.5+0x

​	IGN43F# bTRbBMVzarms8UB1tElRzrRwycSNEJNyk='

3. 校验jwt规则

  1. 解析header,确认alg使用的算法
  2. 签名校验-根据传过来的header和payload按 alg指明的算法进行签名,将签名结果和传过来的sign进行对比,若比对一致,则校验通过
  3. 获取payload自定义内容

JWT和cookie、session相比:

资源
JWT整个过程中除了一个自定义的加密key外没有任何存储的东西,都是计算。
session要将很大的数据存储在服务器中,太浪费资源。

安全
JWT来源性能校验,证明这个东西很难被伪造。具备一定的转码技术,可以让这个token不那么的明文化。

下边是JWT代码的实现和jwt源码基本上一样的,抛离了加减密,jwt主体部分高度一致

import base64
import hmac
import time
import json
import copy


class Jwt():

    @staticmethod  # 静态方法的装饰器封装一下  专门负责做计算用的函数
    def encode(self_payload, key, exp=300):
        # self_payload  含有私有声明的字典
        # key 自定的key
        # exp 过期时间

        # 生成header
        header = {'typ': 'JWT', 'alg': 'HS256'}
        # header_json = json.dumps(header)  # 这样转为json串不行,有空格,损耗带宽
        header_json = json.dumps(header, separators=(',', ':'), sort_keys=True)
        # 这样逗号冒号前后就没有空格了,sort_keys=True 使出来的json串变的有序了,在做hmac或其他哈希的计算的时候,串值一定是稳定的
        # separators分割符 第一个参数代表的是每个键值对之间用什么分割,第二个参数是每个键和值之间用什么分割
        # sort_keys 生成有序的json串
        header_json_base64 = Jwt.b64encode(header_json.encode())

        # init payload
        self_payload_copy = copy.deepcopy(self_payload)  # 为了不污染传进来的字典
        # 给拷贝出来的字典中加入公有声明
        self_payload_copy["exp"] = time.time() + exp  # 过期时间
        self_payload_copy_json = json.dumps(self_payload_copy, separators=(',', ':'), sort_keys=True)
        self_payload_copy_json_base64 = Jwt.b64encode(self_payload_copy_json.encode())

        # init sign
        hm = hmac.new(key.encode(), header_json_base64 + b'.' + self_payload_copy_json_base64,
                      digestmod="SHA256")  # 两个都是字节码所以连接符*点*也要是字节码
        hm_base64 = Jwt.b64encode(hm.digest())  # 取hm的二进制结果,然后进行base64的转码

        # jwt token 诞生  字节码
        return header_json_base64 + b'.' + self_payload_copy_json_base64 + b'.' + hm_base64

    @staticmethod
    def b64encode(js):  # 为了将base64转换修改为urlsafe
        return base64.urlsafe_b64encode(js).replace(b"=", b"")

    @staticmethod
    def b64decode(bs):
        # 加回来等号
        rem = len(bs) % 4  # 取余
        if rem > 0:
            bs += b'=' * (4 - rem)

        return base64.urlsafe_b64decode(bs)

    @staticmethod
    def decode(token, key):
        # 传入jwt的值(令牌) 和只有调用者知道的key

        # 校验签名
        header_bs, payload_bs, signature_bs = token.split(b".")  # 因为是字节串
        hm = hmac.new(key.encode(), header_bs + b"." + payload_bs, digestmod="SHA256")
        if signature_bs != Jwt.b64encode(hm.digest()):  # 将签名结果和传过来的sign进行对比
            raise

        # 校验时间
        payload_js = Jwt.b64decode(payload_bs)  # 解码为json
        payload = json.loads(payload_js)  # 解码为字典

        now = time.time()  # 当前时间
        if int(now) > int(payload["exp"]):  # 登录时间过期
            raise
        return payload  # 返回自定义内容


if __name__ == '__main__':
    # 测试
    s = Jwt.encode({"name": "lyt"}, "1234567", 300)  # 制作令牌
    print(Jwt.decode(s, "1234567"))  # 校验令牌 返回payload明文 字典类型

通过上面的代码,就可以自己写出一个能保持HTTP状态的一个token令牌啦。

如果要测试自己生成的的token令牌是否相对规范和严谨可以到https://jwt.io/自己检查奥!

在这里插入图片描述
分隔符-----------------------------------------------------------------------------------------------------------------------------------------嘿嘿

在这里插入图片描述
前后端分离的结构中解决HTTP协议无状态,使用token可以解决。JWT是token中比较火的一种。所以我们要掌握好哟!!!

少年易老学难成,一寸光阴不可轻。


版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/key_world/article/details/109634148