drf_JWT认证
一、jwt实现过程
1. 构建jwt过程
①、用户提交用户名和密码给服务端,若果登录成功,使用jwt创建一个token串,并返回给用户
eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InpjYyIsImV4cCI6MTU5NDczODg5MX0.OCG4mUhs_yXIkxtxvG9MWJWjpbvnSGDcqMVtpsn_0mo
②、构建三段字符串之间的关系
-
第一段字符串headers:内部包含了算法和token类型
# 流程:先将python类型对象转换成json格式字符串,然后做base64加密 # 内容 headers = { 'typ': 'jwt', 'alg': 'HS256', }
-
第二段字符串payload:自定义的值
# 流程:先将python类型对象转换成json个还是字符串,然后做base64加密 # 内容(可自定义内容) payload = { 'user_id': user.pk, 'username': username, 'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=300), # 超时时间 }
-
第三段字符串signature:将前两段串进行加密
# 第一步:把1,2部分base64加密过后的结果进行拼接加密 # 第二步:对前2部分的加密结果进行hs256加密 + 加盐 # 第三步:对hs256加密后的密文在进行base64url加密再拼接到前1, 2部分base64格式的末尾作为sign.
③、以后用户访问时,需要携带token,后端需要对token进行校验
2. 检验jwt过程
①、获取前端传过来的token
token = request.META.get('HTTP_AUTHORIZATION')
token:eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InpjYyIsImV4cCI6MTU5NDczODg5MX0.OCG4mUhs_yXIkxtxvG9MWJWjpbvnSGDcqMVtpsn_0mo
②、对token进行切割,获取第二段内容进行base64解密,获取payload信息,检查是否超时
# 获得payload串
payload = jwt_decode_handler(token)
# 格式:{'user_id': 1, 'username': 'jason', 'exp': 1611321013, 'email': '3@qq.com'}
③、由于第三部分的字符串不能反解,把第一和第二段在进行hs256加密
1. 把1,2部分base64的密文拼接加密
2. 对前2部分加密进行hs256加密+加盐得到密文
3. 再将密文机进行base64加密, 与前两段的base64d格式的密文进行对比, 如果相等,表示token没有修改通过.
二、drf-jwt安装
1. 官网:http://getblimp.github.io/django-rest-framework-jwt/
2. 安装:pip install djangorestframework-jwt
三、使用内置jwt认证+token签发
1. 快速使用
# 路由中配置
from rest_framework_jwt.views import ObtainJSONWebToken,VerifyJSONWebToken,RefreshJSONWebToken,obtain_jwt_token
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login/', ObtainJSONWebToken.as_view()),
# url(r'^login/', obtain_jwt_token), 和上面的效果一样,
]
-
解析:为什么路由中配置了obtain_jwt_token用户认证, 签发token等等都不需要写了?
# jwt中的views视图类 class JSONWebTokenAPIView(APIView): # 基类,继承了APIView pass class ObtainJSONWebToken(JSONWebTokenAPIView): # 子类,继承JSONWebTokenAPIView # JSONWebTokenSerializer内部就在序列换器里面使用validate钩子, 实现了token的签发 serializer_class = JSONWebTokenSerializer class VerifyJSONWebToken(JSONWebTokenAPIView): # 子类,继承JSONWebTokenAPIView pass class RefreshJSONWebToken(JSONWebTokenAPIView): # 子类,继承JSONWebTokenAPIView pass # 重点 # 看继承关系: obtain_jwt_token = ObtainJSONWebToken.as_view() -> ObtainJSONWebToken -> JSONWebTokenAPIView # JSONWebTokenAPIView就是我们的视图类. 它里面写了post方法, 处理我们的认证请求. obtain_jwt_token = ObtainJSONWebToken.as_view() refresh_jwt_token = RefreshJSONWebToken.as_view() verify_jwt_token = VerifyJSONWebToken.as_view()
2. 使用内置认证快速实现引发的三大缺陷及解决.
①、缺陷1:jwt提供的视图中is_valid校验成功时,无法自定义返回数据格式
-
措施:需要自定义response函数,用来覆盖jwt默认的response函数
# utils.py自定义认证成功返回数据格式 def my_jwt_response_payload_handler(token, user=None, request=None): return { 'token': token, 'msg':'登录成功', 'status':100, 'username':user.username } # settings.py配置文件 JWT_AUTH = { 'JWT_RESPONSE_PAYLOAD_HANDLER':'api.utils.my_jwt_response_payload_handler' }
②、缺陷2:jwt提供的视图中is_valid校验失败时,无法自定义返回数据格式
-
措施:新建视图类,继承ObtainJSONWebToken,重写post方法,使用super拿到response对象,判断是否携带token这个key,有返回成功的response,无,返回失败response
# 路由代码 urlpatterns = [ path('login/', CustomObtainJSONWebToken.as_view()), ] # 视图代码: from rest_framework_jwt.views import ObtainJSONWebToken class CustomObtainJSONWebToken(ObtainJSONWebToken): def post(self, request, *args, **kwargs): response = super().post(request, *args, **kwargs) # 有token表示用户登录认证成功, 返回正确的response. 没有token表示认证失败, 返回错误的response. if response.data.get('token'): obj = CommonResponse(messages='登陆成功', results=response.data) else: obj = CommonResponse(messages='登录失败', results=response.data) return obj
③、缺陷3:jwt提供的认证签发token机制无法实现用户多方式登录
-
措施:新建类基础jwt提供的JSONWebTokenSerializer序列化类, 重写validate方法, 实现用户多方式登录签发token.(基于pyjwt模块)
# 路由代码: from rest_framework_jwt.views import ObtainJSONWebToken, obtain_jwt_token, JSONWebTokenAPIView, VerifyJSONWebToken # import user.views as views from user.views import CustomObtainJSONWebToken urlpatterns = [ path('admin/', admin.site.urls), path('api/', include('user.urls')), path('login_jwt/', ObtainJSONWebToken.as_view()), path('login_custom/', CustomObtainJSONWebToken.as_view()), path('login_custom/', CustomObtainJSONWebToken.as_view()), # path(r'xadmin/', xadmin.site.urls) ] # 视图代码: from rest_framework_jwt.views import ObtainJSONWebToken from .ser import CostomJSONWebTokenSerializer class CustomObtainJSONWebToken(ObtainJSONWebToken): serializer_class = CostomJSONWebTokenSerializer def post(self, request, *args, **kwargs): response = super().post(request, *args, **kwargs) # 有token表示用户登录认证成功, 返回正确的response. 没有token表示认证失败, 返回错误的response. if response.data.get('token'): obj = CommonResponse(messages='登陆成功', results=response.data) else: obj = CommonResponse(code=2000, messages='登录失败', results=response.data) return obj # 序列化器代码: import re import jwt import datetime from django.conf import settings from rest_framework.exceptions import ValidationError from rest_framework_jwt.views import JSONWebTokenSerializer class CostomJSONWebTokenSerializer(JSONWebTokenSerializer): # 使用pyjwt def verify_username(self, username): """多方式登录校验""" if re.search(r'^1[3-9][0-9]{9}$', username): user = models.User.objects.filter(mobile=username).first() elif re.search(r'^.*?@.*?.com$', username): user = models.User.objects.filter(email=username).first() else: user = models.User.objects.filter(username=username).first() if user: return user raise ValidationError("用户名错误!") def verify_password(self, user, password): """校验密码""" is_success = user.check_password(raw_password=password) if not is_success: raise ValidationError("用户密码错误!") def sign_token(self, user): """签发token""" headers = { 'typ': 'jwt', 'alg': 'HS256', } payload = { 'user_id': user.pk, 'username': user.username, 'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=300), } slat = settings.SECRET_KEY token = jwt.encode(payload=payload, key=slat, headers=headers) return token def validate(self, attrs): """校验用户名, 校验密码, 签发token""" username = attrs.get('username') password = attrs.get('password') user = self.verify_username(username) self.verify_password(user, password) token = self.sign_token(user) # 返回什么格式由jwt_response_payload_handler来控制. 因此任然需要重写自定义jwt_response_payload_handler return { 'token': token, 'user': user } # utils/jwt_response.py 自定义认证成功返回格式: from rest_framework_jwt.utils import jwt_response_payload_handler def custom_jwt_response_payload_handler(token, user=None, request=None): # 返回什么, 认证成功时就返回什么格式 return { # 'status': 1000, # 'messages': '登录成功', 'token': token, 'username': user.username, } # 配置文件配置 JWT_AUTH = { 'JWT_RESPONSE_PAYLOAD_HANDLER': 'apps.utils.jwt_response.custom_jwt_response_payload_handler', }
3. 使用内置的认证+控制登录
-
JSONWebTokenAuthentication要和IsAuthenticated连用, 因为不符合内置的认证返回的是None, 那么就获取不到用户对象, 此时是匿名用户IsAuthenticated就对匿名用户做了认证. 因此2个要搭配使用
from rest_framework.views import APIView from rest_framework.response import Response # 内置权限类 # 可以通过认证类JSONWebTokenAuthentication和权限类IsAuthenticated,来控制用户登录以后才能访问某些接口 from rest_framework_jwt.authentication import JSONWebTokenAuthentication # 如果用户不登录就可以访问,只需要把权限类IsAuthenticated去掉即可 from rest_framework.permissions import IsAuthenticated class orderAPIView(APIView): # 认证控制 authentication_classes = [JSONWebTokenAuthentication,] # 权限控制 permission_classes = [IsAuthenticated,] def get(self,request,*args,**kwargs): return Response("这是订单信息,只有登录用户才能访问") class userInfoAPIView(APIView): # 认证控制 authentication_classes = [JSONWebTokenAuthentication, ] # 权限控制,区别在这里 # permission_classes = [IsAuthenticated,] def get(self,request,*args,**kwargs): return Response("这是用户信息,所有用户都能访问")
4. 控制返回数据格式
# utils.py自定义认证成功返回数据格式
def my_jwt_response_payload_handler(token, user=None, request=None):
return {
'token': token,
'msg':'登录成功',
'status':100,
'username':user.username
}
# settings.py配置文件
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER':'api.utils.my_jwt_response_payload_handler'
}
四、自定义jwt认证+token签发
1. 自定义jwt认证
①、继承BaseAuthentication实现
# 继承BaseAuthentication类,在获取user对象时需要自己手动获取
import jwt
from api import models
from rest_framework_jwt.utils import jwt_decode_handler
# from rest_framework_jwt.authentication import jwt_decode_handler # 和上面的是同一个
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication
class MyBaseAuthentication(BaseAuthentication):
def authenticate(self, request):
jwt_value = request.META.get('HTTP_AUTHORIZATION')
# 如果有jwt_value
if jwt_value:
try:
# jwt提供了通过三段token,取出payload的方法,并且有校验功能
payload = jwt_decode_handler(jwt_value)
except jwt.ExpiredSignature:
raise AuthenticationFailed('签名过期')
except jwt.InvalidTokenError:
raise AuthenticationFailed('用户非法')
except Exception as e:
# 所有的异常都会走到这里
raise AuthenticationFailed(str(e))
# 因为payload就是用户信息的字典
print(payload)
# 需要得到user对象
# 第一种:去数据库中查
# user = models.User.objects.filter(pk=payload.get('user_id')).first()
# 第二种:不查库,直接实例一个user对象,此时的user对象功能阉割,字段不全
user = models.User(id=payload.get('user_id'),username=payload.get('username'))
return user,jwt_value
# 没有值,直接抛异常
raise AuthenticationFailed('请你携带认证信息')
# 全局使用
REST_FRAMEWORK = {
# 认证模块
'DEFAULT_AUTHENTICATION_CLASSES': (
'api.utils.MyBaseAuthentication',
),
}
# 局部使用
from api.utils import MyBaseAuthentication
authentication_classes = [MyBaseAuthentication,]
②、继承BaseJSONWebTokenAuthentication 实现
# 继承BaseJSONWebTokenAuthentication,在获取user对象时不需要自己手动获取,直接调用方法即可
import jwt
from api import models
from rest_framework_jwt.utils import jwt_decode_handler
# from rest_framework_jwt.authentication import jwt_decode_handler # 和上面的是同一个
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
class MyBaseJSONWebTokenAuthentication(BaseJSONWebTokenAuthentication):
def authenticate(self, request):
jwt_value = request.META.get('HTTP_AUTHORIZATION')
# 如果有jwt_value
if jwt_value:
try:
# jwt提供了通过三段token,取出payload的方法,并且有校验功能
payload = jwt_decode_handler(jwt_value)
except jwt.ExpiredSignature:
raise AuthenticationFailed('签名过期')
except jwt.InvalidTokenError:
raise AuthenticationFailed('用户非法')
except Exception as e:
# 所有的异常都会走到这里
raise AuthenticationFailed(str(e))
# 因为payload就是用户信息的字典
print(payload)
# 需要得到user对象
user = self.authenticate_credentials(payload)
return user,jwt_value
# 没有值,直接抛异常
raise AuthenticationFailed('请你携带认证信息')
# 全局使用
REST_FRAMEWORK = {
# 认证模块
'DEFAULT_AUTHENTICATION_CLASSES': (
'api.utils.MyBaseAuthentication',
),
}
# 局部使用
from api.utils import MyBaseJSONWebTokenAuthentication
authentication_classes = [MyBaseJSONWebTokenAuthentication,]
③、继承JSONWebTokenAuthentication实现
# 只需要删除一部分,新增一句代码即可
import jwt
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework_jwt.utils import jwt_decode_handler
from django.utils.translation import ugettext as _
from rest_framework import exceptions
class MyJSONWebTokenAuthentication(JSONWebTokenAuthentication):
def authenticate(self, request):
jwt_value = self.get_jwt_value(request)
# 删除这一部分
# if jwt_value is None:
# return None
try:
payload = jwt_decode_handler(jwt_value)
except jwt.ExpiredSignature:
msg = _('Signature has expired.')
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg = _('Error decoding signature.')
raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed()
# 新增这一部分
except Exception as e:
raise exceptions.AuthenticationFailed(str(e))
user = self.authenticate_credentials(payload)
return (user, jwt_value)
# 全局使用
REST_FRAMEWORK = {
# 认证模块
'DEFAULT_AUTHENTICATION_CLASSES': (
'api.utils.MyJSONWebTokenAuthentication',
),
}
# 局部使用
from api.utils import MyJSONWebTokenAuthentication
authentication_classes = [MyJSONWebTokenAuthentication,]
2. 自定义token签发
①、多方式登录,逻辑写在视图中
# 视图代码
from rest_framework.exceptions import ValidationError
from rest_framework import serializers
from rest_framework_jwt.utils import jwt_encode_handler,jwt_payload_handler
from api import models
import re
class LoginAPIView(ViewSet):
"""
继承ViewSet意义:
1. ViewSet = APIView + ViewSetMixin
2. 修改视图类中方法, 使用login明确提意
3. 继承了APIView, 具有较高的可控性
"""
def login(self, request, *args, **kwargs):
username = request.data.get('username')
password = request.data.get('password')
# username=egon/111@qq.com/17621839222
if re.match("^1[3-9][0-9]{9}$",username): # 手机号登录
user = models.User.objects.filter(mobile=username).first()
elif re.match("^.+@.+$",username): # 邮箱登录(只要带有@就认为是邮箱)
user = models.User.objects.filter(email=username).first()
else: # 用户名登录
user = models.User.objects.filter(username=username).first()
# 判断密码是否正确
if user: # 判断用户是否存在
# 此处必须使用check_password,因为密码是密文处理的
if user.check_password(password):
# 此处需要手动签发token串
payload = jwt_payload_handler(user) # 传入user对象,生成payload串
token = jwt_encode_handler(payload) # 传入payload串,生成token串
return Response({'status': 1000, 'token': token, 'results': {'username': user.username, 'email': user.email}})
raise ('用户密码错误!')
raise ValidationError("用户名错误!")
②、多方式登录,逻辑写在序列化类中
# 视图代码
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin,ViewSet
from api import ser
class LoginView(ViewSet):
def login(self,request,*args,**kwargs):
# 1.需要有一个序列化类,生成序列化类对象
login_ser = ser.LoginModelSerializer(data=request.data,context={})
# 2.调用序列化对象的is_valid方法
login_ser.is_valid(raise_exception=True)
token = login_ser.context.get('token')
username = login_ser.context.get('username')
# 3.return返回数据
return Response(data={'status':100,'msg':'成功','token':token,'username':username})
# 序列化器代码
from rest_framework.exceptions import ValidationError
from rest_framework import serializers
from rest_framework_jwt.utils import jwt_encode_handler,jwt_payload_handler
from api import models
import re
class LoginModelSerializer(serializers.ModelSerializer):
# 此处必须覆盖username,因为fields中的username是直接对应表中的username,
# 它是唯一的,如果不覆盖,就会抛出该用户已存在的异常,而不会走到validate方法
username = serializers.CharField()
class Meta:
model = models.User
fields = ['username','password']
# 只要视图类中调用了is_valid方法,就会走validate方法
def validate(self, attrs):
# 在这里写多种登录方式的逻辑
username = attrs.get('username')
password = attrs.get('password')
if re.match("^1[3-9][0-9]{9}$",username): # 手机号登录
user = models.User.objects.filter(mobile=username).first()
elif re.match("^.+@.+$",username): # 邮箱登录(只要带有@就认为是邮箱)
user = models.User.objects.filter(email=username).first()
else: # 用户名登录
user = models.User.objects.filter(username=username).first()
# 判断密码是否正确
if user: # 判断用户是否存在
# 此处必须使用check_password,因为密码是密文处理的
if user.check_password(password):
# 此处需要手动签发token串
payload = jwt_payload_handler(user) # 传入user对象,生成payload串
token = jwt_encode_handler(payload) # 传入payload串,生成token串
self.context['token'] = token
self.context['username'] = user.username
return attrs
else: # 密码不正确,抛出密码错误异常
raise ValidationError("密码错误")
else: # 用后不存在,抛出用户不存在异常
raise ValidationError('当前用户不存在')
"""
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user) # 传入user对象,生成payload串
token = jwt_encode_handler(payload) # 传入payload串,生成token串
"""
③、多方式登录,逻辑写在序列化类中+解耦合
# 路由代码:
url(r'^login/', views.LoginView.as_view(actions={'post': 'login'})),
# 视图代码
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin,ViewSet
from api import ser
class LoginView(ViewSet):
def login(self,request,*args,**kwargs):
# 1.需要有一个序列化类,生成序列化类对象
login_ser = ser.LoginModelSerializer(data=request.data,context={})
# 2.调用序列化对象的is_valid方法
login_ser.is_valid(raise_exception=True)
token = login_ser.context.get('token')
username = login_ser.context.get('username')
# 3.return返回数据
return Response(data={'status':100,'msg':'成功','token':token,'username':username})
# 序列化器代码
import re
from rest_framework import serializers
from rest_framework_jwt.utils import jwt_payload_handler
from rest_framework_jwt.utils import jwt_encode_handler
from rest_framework.exceptions import ValidationError
from . import models
class LoginModelSerializer(serializers.ModelSerializer):
"""登陆接口,jwt方式返回token,格式为{status:100,msg:登陆成功,token:safasdfa}"""
# 此处必须覆盖username,因为fields中的username是直接对应表中的username,
# 它是唯一的,如果不覆盖,就会抛出该用户已存在的异常,而不会走到validate方法
username = serializers.CharField()
class Meta:
model = models.User
fields = ['username','password']
# 使用drf-jwt实现没有解耦合之前
# def validate(self, validate_data):
# print('validate_data:', validate_data)
# username = validate_data.get('username')
# password = validate_data.get('password')
# # 支持多方式登录
# if re.search(r'^1[3-9][0-9]{9}$', username):
# user = models.User.objects.filter(mobile=username).first()
# elif re.search(r'^.*?@.*?.com$', username):
# user = models.User.objects.filter(email=username).first()
# else:
# user = models.User.objects.filter(username=username).first()
#
# if not user.is_delete:
# if user:
# if user.check_password(raw_password=password):
# # 签发token
# payload = jwt_payload_handler(user)
# token = jwt_encode_handler(payload)
# self.context['token'] = token
# self.context['user'] = user
# return validate_data
# else:
# raise ValidationError('用户密码错误!')
# raise ValidationError('用户名错误!')
# raise ValidationError('该用户已经被管理员注销!')
def verify_username(self, username):
"""校验用户不同登录方式"""
if re.match("^1[3-9][0-9]{9}$",username): # 手机号登录
user = models.User.objects.filter(mobile=username).first()
elif re.match("^.+@.+$",username): # 邮箱登录(只要带有@就认为是邮箱)
user = models.User.objects.filter(email=username).first()
else: # 用户名登录
user = models.User.objects.filter(username=username).first()
if user:
return user
raise ValidationError('用户名错误!')
def verify_password(self, user, password):
"""校验密码"""
is_succeed = user.check_password(raw_password=password)
if not is_succeed:
raise ValidationError('用户密码错误!')
def sign_token(self, user):
"""签发token"""
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
self.context['token'] = token
self.context['user'] = user
def validate(self, validate_data):
"""校验用户,密码,签发token接口"""
print('validate_data:', validate_data)
username = validate_data.get('username')
password = validate_data.get('password')
# 校验用户
user = self.verify_username(username)
# 校验密码
self.verify_password(user, password)
# 签发token
self.sign_token(user)
return validate_data
五、jwt参数配置
# settings.py配置信息
JWT_AUTH = {
# token编码方法
'JWT_ENCODE_HANDLER':
'rest_framework_jwt.utils.jwt_encode_handler',
# token解码方法
'JWT_DECODE_HANDLER':
'rest_framework_jwt.utils.jwt_decode_handler',
# 获取payload串方法
'JWT_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_payload_handler',
# 通过payload可以获取user_id
'JWT_PAYLOAD_GET_USER_ID_HANDLER':
'rest_framework_jwt.utils.jwt_get_user_id_from_payload_handler',
# 签名加密,默认是None,就去settings.py中找SECRET_KEY = 'jep)^#5c_@us3#oc@m1c*11dw@zkqn80ku(_d^_%+u382=kz6_'
'JWT_PRIVATE_KEY':
None,
'JWT_PUBLIC_KEY':
None,
'JWT_PAYLOAD_GET_USERNAME_HANDLER':
'rest_framework_jwt.utils.jwt_get_username_from_payload_handler',
'JWT_RESPONSE_PAYLOAD_HANDLER':
'rest_framework_jwt.utils.jwt_response_payload_handler',
'JWT_SECRET_KEY': settings.SECRET_KEY,
'JWT_GET_USER_SECRET_KEY': None,
# 加密方式
'JWT_ALGORITHM': 'HS256',
'JWT_VERIFY': True,
'JWT_VERIFY_EXPIRATION': True,
'JWT_LEEWAY': 0,
# 过期时间,5分钟过期
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=300),
'JWT_AUDIENCE': None,
'JWT_ISSUER': None,
'JWT_ALLOW_REFRESH': False,
# 默认是7天免登陆
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
# 头部访问格式
'JWT_AUTH_HEADER_PREFIX': 'JWT',
'JWT_AUTH_COOKIE': None,
}
六、base64的使用
"""
base64:可变长,可反解
md5:不可变长,不可反解
"""
import base64
import json
# 使用base64进行解密
base64_str = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9'
bytes_json = base64.b64decode(base64_str.encode('utf-8'))
header = json.loads(bytes_json) # {'typ': 'JWT', 'alg': 'HS256'}
print(header)
# 使用base64进行加密
json_str = json.dumps(header)
base64_bytes = base64.b64encode(json_str.encode('utf-8'))
base64_str = base64_bytes.decode('utf-8')
print(base64_str) # eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJIUzI1NiJ9