• 九、drf_JWT认证


    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
    
  • 相关阅读:
    USACO 2019 January Contest Platinum T2: Exercise Route
    USACO 2016 December Contest Gold T3: Lasers and Mirrors
    USACO 2016 December Contest Gold T2: Cow Checklist
    USACO 2016 December Contest Gold T1: Moocast
    USACO 2016 US Open Contest Gold T3: 248
    洛谷p5369[PKUSC2018]最大前缀和
    洛谷p5465 [PKUSC2018]星际穿越
    洛谷p3778[APIO2017]商旅
    NOIP2018提高组题解
    NOIP2017提高组题解
  • 原文地址:https://www.cnblogs.com/borntodie/p/14330829.html
Copyright © 2020-2023  润新知