zoukankan      html  css  js  c++  java
  • drf三大认证和多方式登录

    认证组件

    jwt只负责认证和签发token,对于权限、频率的校验还是走但drf的permmsion和throtting或自定义的校验规则
    
    认证可以配置在局部、全局,但是因为用户登录后所访问的接口都要携带token 进行验证,所以配在全局比较合适,不然我们所写的视图类要一一配置,很麻烦;而权限的校验,根据用户的角色不同,对某接口访问的权限也不同,所以权限的配置只能在具体的视图类中,限制只能某一类角色能访问。
    
    # *局部配置drf-jwt框架的认证类
    from rest_framework_jwt.authentication import JSONWebTokenAuthentication
     1) authentication_classes = [JSONWebTokenAuthentication] # 视图类定义我们的认证类属性
     # * settings文件全局配置drf-jwt框架的认证类
     2) REST_FRAMEWORK = {
        # 认证组件,走jwt的认证类
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'rest_framework_jwt.authentication.JSONWebTokenAuthentication'
        ],
    }
    # 如何自定义认证类、以及认证类规则?
    '''
    1)自定义类继承BaseAuthentication
    	2)重写authenticate方法
    	3)方法体(认证规则):
    		- 从请求头HTTP_AUTHORIZATION中拿token
    		- 没有token,返回None,游客(匿名用户)
    		- 有token,解析异常,抛异常AuthenticationFaild,非法用户  #  前台收到Unauthorized 401
    		- 有token,解析正常,返回(user, token),合法用户
    	使用:全局配置drf-jwt框架的认证类
    '''
    	# 局部配置自定义的认证类,一般在全局settings中配置:
        1) authentication_classes = [authentications.MyAuthentication, ] # 视图类定义我们的认证类属性
        # * settings文件全局配置自定义的认证类
    	2) REST_FRAMEWORK = {
        # 认证组件,走自定义的认证类
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'api.authentication.MyAuthentication'
        ],
    }
    

    权限组件

    
    # 视图类局部配置drf自带或自定义权限类
    class MyAPIView(APIView):
    	permission_classes = [permissions.VIPUserPermission]
    	
    '''
    1.drf自带权限类:IsAuthenticated, IsAdminUser, AllowAny, IsAuthenticatedOrReadOnly
    2.自定义权限类
        1)自定义类继承BasePermission
            2)重写has_permission方法
            3)方法体(权限规则):
                - 根据需求制定判断条件
                - 返回True时,代表有权限
                - 返回False,代表无权限
            使用:局部配置 drf提供的 或 自定义的
    '''
    # permission.py
    class VIPUserPermission(BasePermission):  # 只有vip分组用户有权限
        def has_permission(self, request, view):
            for group in request.user.groups.all():
                if group.name.lower() == 'vip':
                    return True  # 有权限
            return False  # 无权限
    

    频率组件

    
    """
    系统频率类
    1) drf默认提供了一些频率类 
        AnonRateThrottle:只对游客进行频率限制
        UserRateThrottle:对所有用户进行频率限制
    2)如果有特殊需要,需要自定义频率类
        如:对ip进行限次、对电话进行限制、对视图某些信息进行限次
    """
    * 视图类局部配置drf自带或自定义频率类
    class MyAPIView(APIView):
    	throttle_classes = [throttles.MobileRateThrottle]
        
    # throttles.py 自定义频率类
    from rest_framework.throttling import SimpleRateThrottle
    class MobileRateThrottle(SimpleRateThrottle):
        """
        1)1)自定义类继承SimpleThrottle
        2)设置scope字符串类属性,同时在settings配置文件配置scope的字符串值对应的频率限制条件
            REST_FRAMEWORK = {
        # 频率组件:频率类一般做局部配置,但是频率调节在settings中配置
        'DEFAULT_THROTTLE_RATES': {
            'user': '5/min',
            'anon': '3/min',
            'mobile': '1/min'
        },
      }
        3)重写get_catch_key方法:
            返回与限制条件有关的字符串,表示限制(要根据不同的用户,动态变化的)
            返回None,表示不限制(某些条件下,不要进行频率限制)
        """
        scope = 'mobile'
        # rate = '3/min'
        def get_cache_key(self, request, view):  # 继承SimpleRateThrottle类,重写get_cache_key方法
            if not request.user.is_authenticated or not request.user.mobile:
                return None  # 匿名用户 或 没有电话号的用户 都不限制
    
            # 只要有电话号的用户踩进行限制
            return self.cache_format % {
                'scope': self.scope,
                'ident': request.user.mobile
            }  
    
    

    多方式登录并签发token

    '''
    # 多方式登录
    	1)post请求走序列化类,默认当做create动作处理,所以系统字段默认会校验数据库,可能会出现 数据已存在 异常,所以要摒弃数据库的校验,需要自定义反序列化字段(其实就是去掉数据库校验而已)
    	username = serializers.CharField(...)
    	2)多个数据整体操作,不管是校验,还是类似于签发token等逻辑,都可以在全局校验钩子中完成
    	3)多方式登录体现在 请求的账号类型可能是用户名、邮箱或手机等,采用不同字段校验数据库即可
    '''
    
    
    # views.py
    from rest_framework.views import APIView
    from .response import  APIResponse
    from . import serializers
    class LoginAPIView(APIView):
        '''
        1) token只能由登录接口签发
        2) 登录接口也是APIView的子类,使用一定会进行认证、权限、频率组件的校验
        结论:不管系统默认,还是全局settings配置的是何认证与权限组件,登录接口不用参与任何认证与权限的校验
        所以,登录接口一定要进行认证与权限的局部禁用
        '''
        authentication_classes = []
        permission_classes = []
        def post(self,request,*args, **kwargs):
    
            request_data = request.data
            serializer = serializers.LoginModelSerializer(data=request_data)
            serializer.is_valid(raise_exception=True)  # 内部在全局钩子中完成token的签发
            return APIResponse(results={
                'username':serializer.content.get('username'),
                'token':serializer.content.get('token')
            })
        
    # serializers.py
    
    from rest_framework import serializers
    from .import models
    class UserModelSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = models.User
    
            fields = ('username','email','phone')
    
    from rest_framework_jwt.serializers import jwt_payload_handler,jwt_encode_handler
    import re
    class LoginModelSerializer(serializers.ModelSerializer):
        # post请求,序列化默认当作create动作去校验,系统会去校验数据库该字段的的规则,发现该用户名已经存在会抛异常;
        # 抛用户存在异常是多余的,所以自定义系统校验规则即可
        username = serializers.CharField(max_length=64, min_length=3)
        password = serializers.CharField(max_length=20, min_length=3)
        class Meta:
            model = models.User
            fields = ('username','password')
    
        # 用全局钩子,完成token的签发
        def validate(self, attrs):
            # 1) 通过username和password完成多方式登录校验,得到user对象
            user = self._validate_user(attrs)
            # 2) user对象包装payload载荷
            payload = jwt_payload_handler(user)
            # 3) payload载荷签发token
            token = jwt_encode_handler(payload)
            # 4)将user与token存储到serializer对象中,方便在视图类中使用
            self.content = {
                'username': user.username,
                'token': token
            }
            return attrs
    
        def _validate_user(self,attrs):
            username = attrs.get('username')
            password = attrs.get('password')
    
            if re.match(r'.*@*.com',username):# 邮箱
                user = models.User.objects.filter(email=username).first()
            elif re.match(r'1[3-9][0-9]{9}',username):# 电话
                user = models.User.objects.filter(phone=username).first()
            else: # 用户名
                user = models.User.objects.filter(username=username).first()
            if not user or not user.check_password(password):
                raise serializers.ValidationError({'message':'用户信息异常'})
    
            return user
    
  • 相关阅读:
    团队冲刺个人总结第二天
    Gym
    Codeforces Round #162 (Div. 2) A~D 题解
    Wormholes 虫洞 BZOJ 1715 spfa判断负环
    修剪草坪 单调队列优化dp BZOJ2442
    没有上司的舞会 树形dp
    餐巾计划问题 费用流
    最小路径覆盖问题 最大流
    [JSOI2007]麻将 模拟 BZOJ1028
    CF702F T-Shirts FHQ Treap
  • 原文地址:https://www.cnblogs.com/zhangchaocoming/p/12916782.html
Copyright © 2011-2022 走看看