JsonWebToken:pyjwt使用

JsonWebToken:pyjwt使用

jwt官网:https://jwt.io/introduction(搜索jwt)

jwt认证过程:
在这里插入图片描述
单点登录是目前广泛使用JWT的一个特性

JSON Web Token structure

Header
Payload
Signature
形如:xxxxx.yyyyy.zzzzz

Header形如:
{
“alg”: “HS256”,
“typ”: “JWT”
}

第1段:Base64Url 加密

Payload形如:

There are three types of claims(声明): registered, public, and private claims.
Registered claims: 有用的可交互的声明,比如: iss (issuer,发行者), exp (expiration time,到期时间), sub (subject,主题), aud (audience,受众), and others.
{
“sub”: “1234567890”,
“name”: “John Doe”,
“admin”: true
}
可以存放如:id、exp(过期时间)等参数

Signature形如:

For example if you want to use the HMAC SHA256 algorithm, the signature will be created in the following way:

HMACSHA256(
base64UrlEncode(header) + “.” +
base64UrlEncode(payload),
secret)

只要加盐的盐不暴露,后端拿到前端传过来的token,后端根据第1和第2段来加盐加密,和前端传过来的第3段对比,相等即验证成功

注意:base64url加密是先做base64加密,然后再将 - 替代 + 及 _ 替代 /

python使用jwt
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
生成不重复的随机数,可以使用微秒级时间戳生成

time.time()获取的是秒级的时间戳,小数点后保留6位,最多可以保留到微秒级

import time
x = lambda : int(time.time()* 1000*1000)

安装pyjwt
在这里插入图片描述
Successfully installed pyjwt-2.1.0

pyjwt官方文档

https://pyjwt.readthedocs.io/en/stable/

pyjwt使用

import jwt
import datetime
from jwt import exceptions
import time
import uuid


# uuid.uuid1()获得的是uuid.UUID对象,盐要转换成str,str长度为36
# uuid.uuid1():基于MAC地址,时间戳,随机数来生成唯一的uuid,可以保证全球范围内的唯一性
def create_token():
    SALT = str(uuid.uuid1())
    print("盐:", SALT)
    globals()["salt"] = SALT
    # 构造header
    headers = {
        'alg': "HS512",
        'typ': "JWT"
    }
    # 构造payload
    fmt = "%Y-%m-%d %H:%M:%S"
    # 过期时间(北京时间)
    # expiration_time_str = (datetime.datetime.now() + datetime.timedelta(minutes=2)) \
    #     .strftime(fmt)
    # 过期时间戳,time.mktime():float
    # exp_timestamp = time.mktime(time.strptime(expiration_time_str, fmt))
    # now_time = datetime.datetime.strptime(expiration_time_str, fmt)
    # print("now_time:", now_time)
    # print("exp_timestamp", exp_timestamp)
    utc_now_str = (datetime.datetime.utcnow() + datetime.timedelta(minutes=20)).strftime(fmt)
    utc_now = datetime.datetime.strptime(utc_now_str, fmt)
    payload = {
        'sub': "x" + str(int(time.time())),
        'exp': utc_now
        # 'exp': exp_timestamp
    }
    print("payload", payload)
    result = jwt.encode(headers=headers, payload=payload, key=SALT, algorithm="HS512")
    return result

注意:pyjwt的payload中,过期时间必须是utc时间(官方文档中说明需要使用utc),而且直接传入datetime时间对象即可,或者传入时间戳也可以(秒级),然后在使用jwt.decode方法获取payload时,就可以拿到准确的过期时间戳(时间戳:秒级,10位,毫秒级是13位,jwt.decode是获取秒级的时间戳),如果在使用jwt.encode时,使用的是北京时间的过期日期或时间戳,那么jwt.decode获取的时间戳依旧会增加8小时,就与自己原本的需求不相符合了

一般在认证成功后,把jwt生成的token返回给用户,以后用户再次访问时候需要携带token,此时jwt需要对token进行超时合法性校验。

获取token之后,会按照以下步骤进行校验:

将token分割成 header_segment、payload_segment、crypto_segment 三部分

header_segment, payload_segment, crypto_segment = token.split(".")

对第一部分header_segment进行base64url解密,得到header

对第二部分payload_segment进行base64url解密,得到payload

对第三部分crypto_segment进行base64url解密,得到signature

对第三部分signature部分数据进行合法性校验

拼接前两段密文,即:signing_input
从第一段明文中获取加密算法,默认:HS256
使用 算法+盐 对signing_input 进行加密,将得到的结果和signature密文进行比较。

def check_token(token):
    try:
        verified_payload = jwt.decode(token, globals()["salt"], algorithms="HS512")
        return verified_payload
    except:
        pass

if __name__ == '__main__':
    t = create_token()
    print(t)
    a = check_token(t)
    print(a)
    print("过期时间", datetime.datetime.fromtimestamp(a['exp']))
# 结果
# 在2021-07-18 15:36执行,过期时间+20mins,所以是2021-07-18 15:56:21: d916dac1-e79a-11eb-a95c-14f6d8e4b681
payload {'sub': 'x1626593781', 'exp': datetime.datetime(2021, 7, 18, 7, 56, 21)}
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ4MTYyNjU5Mzc4MSIsImV4cCI6MTYyNjU5NDk4MX0.y3a8dfHDyRurCFxPv3bu8407YBJsT02ZoeQIgl_8fo-tM3-2yqfZjiv9XCsEVmnXxCXIPy-j2zv5BJrf5gi3Lw
{'sub': 'x1626593781', 'exp': 1626594981}
过期时间 2021-07-18 15:56:21

完善token的校验

1 超过过期时间,但是校验token的盐没有问题,token也没有被修改过,就会报错:jwt.exceptions.ExpiredSignatureError: Signature has expired(token失效)

2 如果盐有问题,或者盐没问题,但是token被修改过,都会报错:jwt.exceptions.InvalidSignatureError: Signature verification failed(非法token)

tips:pyjwt校验时,如果同时存在上述1和2的错误,即签名不对,并且还过期了,优先提示的是 jwt.exceptions.InvalidSignatureError,签名错误,而非token过期

3 校验的token的格式不对:

3.1 前端传的token是"null",或者空字符串,或者"1.2"等等,只要不满足使用split(".")切割能够拿到3个segment,都会报错:jwt.exceptions.DecodeError: Not enough segments

3.2 前端传的token满足split(".")切割能够拿到3个segment,但是是乱写的,比如:"1.2.3"等等,会报错:jwt.exceptions.DecodeError: Invalid header padding,Invalid crypto padding等等,哪怕是1和2部分格式正确了,但是3不对(后端使用自身知晓的盐进行校验,和前端返回来的使用过盐拿到的3对不上,报错jwt.exceptions.InvalidSignatureError,签名无效)

查看源码,大致有如下的异常:
在这里插入图片描述
修改后的token校验:

def check_token(token):
    try:
        verified_payload = jwt.decode(token, globals()["salt"], algorithms="HS512")
        return verified_payload
    except exceptions.InvalidSignatureError:
        print({"code": "601", "error": "非法token!"})
    except exceptions.ExpiredSignatureError:
        print({"code": "602", "error": "token失效!"})
    except exceptions.DecodeError:
        print({"code": "603", "error": "token解析异常!"})
    except Exception as e:
        print({"code": "604", "error": "token错误!", "msg": str(e)})
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值