zoukankan      html  css  js  c++  java
  • drf☞jwt自动签发与手动签发

    一、自动签发

    urls

    from rest_framework_jwt.views import obtain_jwt_token
    # 使用jwt自带的登录视图
    urlpatterns = [
        path('login/', obtain_jwt_token),
    ]
    

    settings

    import datetime
    JWT_AUTH={
        # 配置响应格式,必须和自动签发使用
      'JWT_RESPONSE_PAYLOAD_HANDLER':'app01.utils.my_jwt_response_payload_handler',
        'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # 过期时间,手动配置
    }
    

    utils

    # 重写jwt响应格式(需要到settings配置)
    # 与之配合使用的必须是自动签发
    def my_jwt_response_payload_handler(token, user=None, request=None): # 返回什么,前端就能看到什么样子
        return {
            'token': token,
            'msg':'登录成功',
            'status':100,
            'username':user.username
    
        }
    

    然后直接在前端提交post请求发送账号和密码,会返回我们定义好的响应格式

    {
        "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Imh6IiwiZXhwIjoxNTk1NDE1MTEyLCJlbWFpbCI6IiJ9.BxBdsm6oBz8iPAwSSpo_7IaU4pBp6RjK4c0GJ_FYN1E",
        "msg": "登录成功",
        "status": 100,
        "username": "hz"
    }
    

    然后拿出token对测试类发送测试请求

    class TestAPI(APIView):
        def get(self,request):
            print(request.user)
            return Response("ok")
    # 因为内置的他没有对匿名用户设置拦截,素以匿名用户也能看到ok
    # 我们用request.user来区分
    # 这里可能会出现我登录了很多次,用每次不同的token都能登录
    # 这是因为token校验的是规则,是要加密规则符合且没有超时,那用哪次token都一样的
    

    二、手动签发

    utils

    from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication,jwt_decode_handler
    import jwt
    from rest_framework.exceptions import AuthenticationFailed
    class MyAuthentication(BaseJSONWebTokenAuthentication):
        # 这里重写的逻辑和BaseJSONWebTokenAuthentication里的authenticate一模一样
        def authenticate(self, request):
            jwt_token = request.META.get('HTTP_AUTHORIZATION') # 获取浏览器传来的token
            if jwt_token:
                try:
                    payload = jwt_decode_handler(jwt_token) # 传入token,拿出第二段用户信息,有内置的校验token功能
                except jwt.ExpiredSignature:
                    raise AuthenticationFailed('签名过期')
                except jwt.InvalidTokenError:
                    raise AuthenticationFailed('用户非法')
                except Exception as e:
                    # 所有异常都会走到这
                    raise AuthenticationFailed(str(e))
                # 通过内置的方法把payload转换成用户对象
                user = self.authenticate_credentials(payload)
                return user,None # ===》request.user,request.auth
            raise AuthenticationFailed('您没有携带认证信息')
    

    sers

    from rest_framework import serializers
    
    
    # 多方序列化校验登录
    import re
    from rest_framework.exceptions import ValidationError
    from app01 import models
    from rest_framework_jwt.utils import jwt_encode_handler,jwt_payload_handler
    class LoginSer(serializers.ModelSerializer):
        # 我们要提交校验数据的时候,如果直接用下面Meta绑定给模型类的话
        # 关键点2:这里如果不写username的话,序列化器直接用的是模型类的username
        # 这两者的区别在于,如果覆盖写了username,他表示的可以是任何前端传来的数据,如果是模型类绑定,那只能是用户名了
        # 我们这里username用于多方登录的校验数据,必须要重写
        # 而password不用重写,因为password用的就是模型类本身的
        username = serializers.CharField()
        class Meta:
            model = models.User
            fields =['username','password']
        def validate(self, attrs):
            username = attrs.get('username')
            password = attrs.get('password')
            if 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:
                    if user.check_password(password):
                        # 关键点3:jwt_payload_handler把用户数据对象转化成用户信息的字典
                        # jwt_encode_handler把用户信息的字典转化成token
                        payload = jwt_payload_handler(user)
                        # print('user:',user,type(user))
                        token = jwt_encode_handler(payload)
                        # print('payload:',payload,type(payload))
                        # print('token:',token)
                        # 关键点4:如果我们要给序列化器添加数据,让视图函数去使用
                        # 通常都是传给对象的context属性,当然直接赋值也可以,这只是他给我们提供的传值接口
                        self.context['token'] = token
                        self.context['user'] = user
                        self.user = user
                        return attrs
                    else:
                        raise ValidationError('密码错误')
                raise ValidationError('不存在用户')
            raise ValidationError('请输入用户名')
    
    
    

    views

    class LoginApi(ViewSet):
        authentication_classes = []
        def login(self, request):
            # 在调用序列化类给context传数据,可以直接在序列化类中调用
            # 关键点1:注意区分序列化传值与反序列化
            # 这里只要拿字典取校验数据,那就传给data
            # 如果是要把数据对象转化成字典就传给instance
            user_ser = sers.LoginSer(data=request.data, context={'request': request})
            user_ser.is_valid(raise_exception=True)
            token = user_ser.context.get('token')
            user = user_ser.context.get('user')
            print(user_ser.user)
            return Response({'code': 100, 'msg': '登录成功', 'token': token, 'username': user.username})
    
  • 相关阅读:
    什么是云安全
    VMWare vForum 2013看点
    循环和数据的操作命令
    程序交互
    数据类型
    基础变量
    模块和包
    ['hello', 'sb']正则表达式
    os模块
    内置函数
  • 原文地址:https://www.cnblogs.com/hz2lxt/p/13307504.html
Copyright © 2011-2022 走看看