Python用户认证JWT——PyJWT

本文详细介绍了Python库PyJWT的使用,包括JWT的创建、HS256和RS256编解码、自定义头部、签名校验以及JWT的标准字段如过期时间、生效时间、签发人和受众等。还展示了如何处理JWT的过期和未生效情况,并探讨了从JWKS端点获取RSA签名密钥的方法。此外,文中还讨论了JWT的数字签名算法和常见问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

PyJWT 是一款 Python 编解码 JWT 的库

JWT,JSON Web Token,基于 RFC7519,用于跨域认证 ,便于服务器集群认证

服务器认证后,生成一个 JWT 返回客户端,之后客户端与服务器通信都要发回这个 JSON,用于身份认证

客户端收到服务器返回的 JWT,可以存在 Cookie 里,也可以存在 localStorage

客户端通信时,可以放在 Cookie 里,也可以放在 Headers 的 Authorization 里,后一种可以解决跨域问题

JWT. 分隔,分别为:

  • Header(头部)
    • 描述 JWT 的元数据,如 {"alg": "HS256", "typ": "JWT"}
    • alg :生成签名算法,默认为 HS256
    • typ:令牌类型,默认为 JWT
  • Payload(负载)
    • 存放实际数据,官方字段如下
    • exp (Expiration Time):过期时间
    • nbf (Not Before Time):生效时间
    • iss (Issuer):签发人
    • aud (Audience):受众
    • iat (Issued At):签发时间
    • sub (Subject):主题
    • jti (JWT ID):编号
  • Verify Signature(签名)

推荐阅读:




安装

pip install PyJWT




初试

import jwt

encoded_jwt = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256')  # 编码
decoded_jwt = jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])  # 解码
print(encoded_jwt)  # eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzb21lIjoicGF5bG9hZCJ9.Joh1R2dYzkRvDkqv3sygm5YyK8Gi4ShZqbhK2gxcs2U
print(decoded_jwt)  # {'some': 'payload'}




HS256编解码

多数 JWT 使用对称算法 HS256 生成

import jwt

encoded_jwt = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256')  # 编码
decoded_jwt = jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])  # 解码
print(encoded_jwt)  # eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzb21lIjoicGF5bG9hZCJ9.Joh1R2dYzkRvDkqv3sygm5YyK8Gi4ShZqbhK2gxcs2U
print(decoded_jwt)  # {'some': 'payload'}




RS256编解码

RS256 是非对称加密算法

安装

pip install cryptography

代码

import jwt
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa

private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
private_key = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.TraditionalOpenSSL,
    encryption_algorithm=serialization.NoEncryption()
)
public_key = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)

encoded_jwt = jwt.encode({'some': 'payload'}, private_key, algorithm='RS256')  # 编码
decoded_jwt = jwt.decode(encoded_jwt, public_key, algorithms=['RS256'])  # 解码
print(encoded_jwt)
print(decoded_jwt)  # {'some': 'payload'}




自定义头部

import jwt

encoded_jwt = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256',
                         headers={'kid': '230498151c214b788dd97f22b85410a5'})  # 编码
decoded_jwt = jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])  # 解码
print(encoded_jwt)
print(decoded_jwt)  # {'some': 'payload'}




不进行签名校验

参数 options={'verify_signature': False} 不进行签名校验

import jwt

encoded_jwt = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256')  # 编码
decoded_jwt = jwt.decode(encoded_jwt, options={'verify_signature': False})  # 解码
print(encoded_jwt)
print(decoded_jwt)  # {'some': 'payload'}




读取头部不进行签名校验

import jwt

encoded_jwt = jwt.encode({'some': 'payload'}, 'secret', algorithm='HS256')  # 编码
unverified_header = jwt.get_unverified_header(encoded_jwt)  # 读取头部
print(unverified_header)  # {'typ': 'JWT', 'alg': 'HS256'}




声明负载字段

支持以下字段

过期时间

exp (Expiration Time):过期时间,可用 UTC 时间戳或 datetime

解析超时报错 jwt.ExpiredSignatureError

参数 options={'verify_exp': False} 不进行过期时间校验

import datetime

import jwt

timestamp = int(datetime.datetime(2022, 1, 1).timestamp())  # 2022-01-01的时间戳,类型为int
after_a_week = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(days=7)  # 一周后,类型为datetime

# 超时报错
encoded_jwt = jwt.encode({'exp': timestamp}, 'secret', algorithm='HS256')
try:
    jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])
except jwt.ExpiredSignatureError as e:
    print(e)  # Signature has expired
decoded_jwt = jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'], options={'verify_exp': False})  # 不进行过期时间校验
print(decoded_jwt)  # {'exp': 1640966400}

# 正常解析
encoded_jwt = jwt.encode({'exp': after_a_week}, 'secret', algorithm='HS256')
decoded_jwt = jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])
print(decoded_jwt)  # {'exp': 1663252132}



生效时间

nbf (Not Before Time):生效时间,可用 UTC 时间戳或 datetime

解析未生效报错 jwt.ExpiredSignatureError

参数 options={'verify_nbf': False} 不进行过期时间校验

import datetime

import jwt

timestamp = int(datetime.datetime(2022, 1, 1).timestamp())  # 2022-01-01的时间戳,类型为int
after_a_week = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(days=7)  # 一周后,类型为datetime

# 未生效报错
encoded_jwt = jwt.encode({'nbf': after_a_week}, 'secret', algorithm='HS256')
try:
    jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])
except jwt.ImmatureSignatureError as e:
    print(e)  # The token is not yet valid (nbf)
decoded_jwt = jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'], options={'verify_nbf': False})  # 不进行生效时间校验
print(decoded_jwt)  # {'nbf': 1663252132}

# 正常解析
encoded_jwt = jwt.encode({'nbf': timestamp}, 'secret', algorithm='HS256')
decoded_jwt = jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])
print(decoded_jwt)  # {'nbf': 1640966400}



签发人

iss (Issuer):签发人

签发人不匹配报错 jwt.InvalidIssuerError

import jwt

payload = {'some': 'payload', 'iss': 'urn:foo'}

encoded_jwt = jwt.encode(payload, 'secret', algorithm='HS256')

# 未生效报错
try:
    jwt.decode(encoded_jwt, 'secret', issuer='tli:foo', algorithms=['HS256'])
except jwt.InvalidIssuerError as e:
    print(e)  # Invalid issuer

# 正常解析
decoded_jwt = jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])
print(decoded_jwt)  # {'some': 'payload', 'iss': 'urn:foo'}



受众

aud (Audience):受众

受众不匹配报错 jwt.InvalidAudienceError

import jwt

payload = {'some': 'payload', 'aud': ['urn:foo', 'urn:bar']}

encoded_jwt = jwt.encode(payload, 'secret', algorithm='HS256')

# 未生效报错
try:
    jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])
except jwt.InvalidAudienceError as e:
    print(e)  # Invalid audience

# 正常解析
decoded_jwt = jwt.decode(encoded_jwt, 'secret', audience='urn:foo', algorithms=['HS256'])
print(decoded_jwt)  # {'some': 'payload', 'aud': ['urn:foo', 'urn:bar']}



签发时间

iat (Issued At):签发时间

签发时间非数字或 datetime 报错 jwt.InvalidIssuedAtError

import datetime

import jwt

now = int(datetime.datetime.now(tz=datetime.timezone.utc).timestamp())
payload = {'some': 'payload', 'iat': now}

encoded_jwt = jwt.encode(payload, 'secret', algorithm='HS256')
decoded_jwt = jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'])
print(decoded_jwt)  # {'some': 'payload', 'iat': 1662620247}




必填负载字段

必填 expisssub

import datetime

import jwt

after_a_week = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(days=7)  # 一周后,类型为datetime
payload = {'some': 'payload', 'exp': after_a_week, 'iss': 'urn:foo', 'sub': 'foo'}

encoded_jwt = jwt.encode(payload, 'secret', algorithm='HS256')
decoded_jwt = jwt.decode(encoded_jwt, 'secret', algorithms=['HS256'], options={'require': ['exp', 'iss', 'sub']})
print(decoded_jwt)  # {'some': 'payload', 'exp': 1663225440, 'iss': 'urn:foo', 'sub': 'foo'}




从JWKS端点检索RSA签名密钥

JWKS,JSON Web Key Set,指多个 JWK 组合在一起

import jwt
from jwt import PyJWKClient

token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRTFRVVJCT1RNNE16STVSa0ZETlRZeE9UVTFNRGcyT0Rnd1EwVXpNVGsxUWpZeVJrUkZRdyJ9.eyJpc3MiOiJodHRwczovL2Rldi04N2V2eDlydS5hdXRoMC5jb20vIiwic3ViIjoiYVc0Q2NhNzl4UmVMV1V6MGFFMkg2a0QwTzNjWEJWdENAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vZXhwZW5zZXMtYXBpIiwiaWF0IjoxNTcyMDA2OTU0LCJleHAiOjE1NzIwMDY5NjQsImF6cCI6ImFXNENjYTc5eFJlTFdVejBhRTJINmtEME8zY1hCVnRDIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.PUxE7xn52aTCohGiWoSdMBZGiYAHwE5FYie0Y1qUT68IHSTXwXVd6hn02HTah6epvHHVKA2FqcFZ4GGv5VTHEvYpeggiiZMgbxFrmTEY0csL6VNkX1eaJGcuehwQCRBKRLL3zKmA5IKGy5GeUnIbpPHLHDxr-GXvgFzsdsyWlVQvPX2xjeaQ217r2PtxDeqjlf66UYl6oY6AqNS8DH3iryCvIfCcybRZkc_hdy-6ZMoKT6Piijvk_aXdm7-QQqKJFHLuEqrVSOuBqqiNfVrG27QzAPuPOxvfXTVLXL2jek5meH6n-VWgrBdoMFH93QEszEDowDAEhQPHVs0xj7SIzA'
kid = 'NEE1QURBOTM4MzI5RkFDNTYxOTU1MDg2ODgwQ0UzMTk1QjYyRkRFQw'
url = 'https://dev-87evx9ru.auth0.com/.well-known/jwks.json'
jwks_client = PyJWKClient(url)
signing_key = jwks_client.get_signing_key_from_jwt(token)
data = jwt.decode(
    token,
    signing_key.key,
    algorithms=['RS256'],
    audience='https://expenses-api',
    options={'verify_exp': False},
)
print(data)




常见问题




数字签名算法




参考文献

  1. PyJWT Documentation
  2. Cryptography Documentation
  3. JSON Web Token 入门教程 - 阮一峰的网络日志
  4. Python JWT使用
  5. RS256 vs HS256 What’s the difference?
  6. Python日期和时间库datetime
  7. Python时间访问和转换库time
  8. JWT实现登陆认证及Token自动续期
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XerCis

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值