zoukankan      html  css  js  c++  java
  • drf-认证权限频率

    一 认证Authentication

    1.1 自定义认证方案

    认证的实现

    1 写一个类,继承BaseAuthentication,重写authenticate,认证的逻辑写在里面,认证通过,返回两个值,一个值最终给了Requet对象的user,认证失败,抛异常:APIException或者AuthenticationFailed

    2 全局使用,局部使用

    案例:

    登陆接口,查询图书接口,必须登录后才能查看,token信息放在头里(认证组件),全局使用,局部禁用(login禁用)

    使用simplerouter自动生成路由

    1.1.1 编写models

    class User(models.Model):
        username=models.CharField(max_length=32)
        password=models.CharField(max_length=32)
        user_type=models.IntegerField(choices=((1,'超级用户'),(2,'普通用户'),(3,'二笔用户')))
    
    class UserToken(models.Model):
        user=models.OneToOneField(to='User')
        token=models.CharField(max_length=64)

    1.1.2 新建认证类

    在app下新建一个py文件

    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from app01.models import UserToken
    
    
    class MyAuthentication(BaseAuthentication):
        def authenticate(self, request):
            token = request.META.get('HTTP_TOKEN')
            if token:
                user_token = UserToken.objects.filter(token=token).first()
                if user_token:
                    return user_token.user, token
                else:
                    raise AuthenticationFailed('认证失败')
            else:
                raise AuthenticationFailed('请求头中需要携带token')

    1.1.3 编写视图

    from rest_framework.decorators import action
    from app01 import models
    from rest_framework.views import APIView
    import uuid
    from app01.ser import BookSerializers
    from rest_framework.viewsets import ModelViewSet
    from rest_framework.response import Response
    class Login(APIView):
        authentication_classes = []#意思是局部禁用
        def post(self, request):
            username = request.data.get('username')
            password = request.data.get('password')
            user = models.users.objects.filter(username=username, password=password).first()
            if user:
                token = uuid.uuid4()
                models.UserToken.objects.update_or_create(defaults={'token': token}, user=user)
                return Response({'status': 100, 'msg': '登陆成功', 'token': token})
            else:
                return Response({'status': 101, 'msg': '登陆失败'})
    
    
    # 使用ModelViewSet编写5个接口
    class Bookview(ModelViewSet):
        queryset = models.book.objects.all()
        serializer_class = BookSerializers
    
        @action(methods=['GET', 'POST'], detail=True)
        def get_10(self,request,pk):
            book = self.get_queryset()[:10]  # 从0开始截取一条
            ser = self.get_serializer(book, many=True)
            return Response(ser.data)

    1.1.4 编写序列化器

    from rest_framework import serializers
    from app01.models import book
    
    
    class BookSerializers(serializers.ModelSerializer):
        class Meta:
            model = book
            fields = '__all__'

    1.1.5 全局使用

    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",]
    }

    1.1.6 局部使用

    #局部使用,只需要在视图类里加入:
    authentication_classes = [MyAuthentication]

    1.2 认证的源码分析

    在APIView下的dispath方法中有request = self.initialize_request(request, *args, **kwargs),这句代码重写了request,并在request中加入了authenticators=self.get_authenticators()。让我们来分析一下这段代码:

    def get_authenticators(self):
            """
            Instantiates and returns the list of authenticators that this view can use.
            """
            return [auth() for auth in self.authentication_classes]

    get_authenticators()只有简单的那么一句代码,这句代码是一个标准列表生成式,他的意思是遍历self.authentication_classes加括号执行,并存放到列表当中。那么self.authentication_classes里面又有什么东西呢?

    点进去我们看见是authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES,他的意思是从配置文件 中找到取出值赋值给authentication_classes这个列表,而这个列表中的值就是认证方法。默认的认证方法是无效的,因此我们要自己定义个authentication_classes并在里面写入自己定义的认证方法。

    在drf中取数据的顺序是先从自己定义的类中查找,找不到再去项目下的settings.py文件中查找,最后再去框架中的配置文件中查找。知道了这个后就能很清楚的理解为何要自己定义authentication_classes和全局使用,局部使用,局部禁用,全局禁用的原理了。举个例子:上面views.py的代码中我写了authentication_classes = [],根据类方法的自调用[auth() for auth in self.authentication_classes]中的self.authentication_classes指的就是我自己写的空列表,列表为空,生成式也为空,那么到认证代码也是空,这就达到了局部禁用的效果。(个人觉得这里最绕的还是类的自调用啥的,所以还是再去好好学下类吧….)

    回归主题知道了authentication_classes中放的是你写的认证类之后,那[auth() for auth in self.authentication_classes]这代码的执行结果应该是认证类的对象列表,递推回去那么authenticators=self.get_authenticators()中authenticators就是认证类的对象列表,经过绕来绕去最后到了request对象下的_authenticate中。

    -request.py下的   
    def _authenticate(self):
            # 遍历拿到一个个认证器,进行认证
            #self.authenticators 你在视图类中配置的一个个的认证类:authentication_classes=[认证类1,认证类2],对象的列表
            for authenticator in self.authenticators:
                try:
                    # 认证器(对象)调用认证方法authenticate(认证类对象self, request请求对象)
                    # 返回值:登陆的用户与认证的信息组成的 tuple
                    # 该方法被try包裹,代表该方法会抛异常,抛异常就代表认证失败
                    user_auth_tuple = authenticator.authenticate(self) #注意这self是request对象
                except exceptions.APIException:
                    self._not_authenticated()
                    raise
    
                # 返回值的处理
                if user_auth_tuple is not None:
                    self._authenticator = authenticator
                    # 如何有返回值,就将 登陆用户 与 登陆认证 分别保存到 request.user、request.auth
                    self.user, self.auth = user_auth_tuple
                    return
            # 如果返回值user_auth_tuple为空,代表认证通过,但是没有 登陆用户 与 登陆认证信息,代表游客
            self._not_authenticated()

    上面这段代码就是核心代码了

    二 权限Permissions

    权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。

    • 在执行视图的dispatch()方法前,会先进行视图访问权限的判断
    • 在通过get_object()获取具体对象时,会进行模型对象访问权限的判断

    2.1 自定义权限

    2.1.1 编写权限类

    写一个类,继承BasePermission,重写has_permission,如果权限通过,就返回True,不通过就返回False

    class MyPermission(BasePermission):
        def has_permission(self, request, view):
            # 不是超级用户,不能访问
            # 由于认证已经过了,request内就有user对象了,当前登录用户
            user = request.user
            # 如果该字段用了choice,通过get_字段名_display()就能取出choice后面的中文
            # print(user.get_user_type_display())
            if user.user_type==1:
                return True
            else:
                return False

    2.1.2 全局使用

    REST_FRAMEWORK={
         # 认证类的全局使用
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",],
         # 权限类的全局使用
        'DEFAULT_PRESSION_CLASSES':['app01.app_auth.MyPermission,']
    }

    2.1.3 局部使用和禁用

    # 局部使用只需要在视图类里加入:
    permission_classes = [MyPermission,]
    # 局部禁用只需要在视图类里加入:
    permission_classes = []

    2.1.4 说明

    如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部

    has_permission(self, request, view)是否可以访问视图, view表示当前视图对象

    has_object_permission(self, request, view, obj)是否可以访问数据对象, view表示当前视图, obj为数据对象

    2.2 权限源码分析

    APIView—->dispatch—->initial—>self.check_permissions(request)(APIView的对象方法)通过上述的一层层查找我们能找到权限的核心源码

     def check_permissions(self, request):
            """
            Check if the request should be permitted.
            Raises an appropriate exception if the request is not permitted.
            """
            for permission in self.get_permissions():
                if not permission.has_permission(request, self):
                    self.permission_denied(
                        request, message=getattr(permission, 'message', None)
                    )

    权限的核心代码很多地方其实和认证的差不多。self.get_permissions()点进去后会发现他的源码和认证的几乎一样,这就代表着他存放的是权限类的对象集合成的列表。

    遍历一个个权限列表得到一个个权限对象(权限器),进行权限认证。

    权限类一定有一个has_permission权限方法,用来做权限认证的。

    参数:权限对象self、请求对象request、视图类对象

    返回值:有权限返回True,无权限返回False 如果返回值不是True就代表他没权限进行限制访问

    2.3 内置权限(了解)

    演示一下内置权限的使用:IsAdminUser,控制是否对网站后台有权限的人

    # 1 创建超级管理员
    # 2 写一个测试视图类
    from rest_framework.permissions import IsAdminUser
    from rest_framework.authentication import SessionAuthentication
    class TestView3(APIView):
        authentication_classes=[SessionAuthentication,]
        permission_classes = [IsAdminUser]
        def get(self,request,*args,**kwargs):
            return Response('这是22222222测试数据,超级管理员可以看')
    # 3 超级用户登录到admin,再访问test3就有权限
    # 4 正常的话,普通管理员,没有权限看(判断的是is_staff字段)

    三 频率(限流)

    可以对接口访问的频次进行限制,以减轻服务器压力。

    一般用于付费购买次数,投票等场景使用.

    3.1 内置频率类

    3.1.1 AnonRateThrottle

    限制所有匿名未认证用户,使用IP区分用户。

    使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次

    -views.py
    from rest_framework.permissions import IsAdminUser
    from rest_framework.authentication import BasicAuthentication
    class TestView4(APIView):
        authentication_classes=[]
        permission_classes = []
        def get(self,request,*args,**kwargs):
            return Response('我是未登录用户')
    
    # 全局使用
    -settings.py
    REST_FRAMEWORK = {
        'DEFAULT_THROTTLE_CLASSES': (
            'rest_framework.throttling.AnonRateThrottle',
        ),
        'DEFAULT_THROTTLE_RATES': {
            'anon': '3/m',# 使用 `second`, `minute`, `hour` 或`day`来指明周期。
        }
    }
    
    # 局部使用
    -views.py
    class TestView4(APIView):
       ...
       throttle_classes = [AnonRateThrottle]
       def get(self,request,*args,**kwargs):
            return Response('我是未登录用户')

    3.1.2 UserRateThrottle

    限制认证用户,使用User id 来区分。

    使用DEFAULT_THROTTLE_RATES['user'] 来设置频次

    全局:在setting中
      'DEFAULT_THROTTLE_CLASSES': (
            'rest_framework.throttling.UserRateThrottle'
        ),
        'DEFAULT_THROTTLE_RATES': {
            'user': '10/m',
        }
            
    局部配置:
    在视图类中配一个就行

    3.1.3 ScopedRateThrottle

    限制用户对于每个视图的访问频次,使用ip或user id。

    class ContactListView(APIView):
        throttle_scope = 'contacts'
        ...
    
    class ContactDetailView(APIView):
        throttle_scope = 'contacts'
        ...
    
    class UploadView(APIView):
        throttle_scope = 'uploads'
        ...
    REST_FRAMEWORK = {
        'DEFAULT_THROTTLE_CLASSES': (
            'rest_framework.throttling.ScopedRateThrottle',
        ),
        'DEFAULT_THROTTLE_RATES': {
            'contacts': '1000/day',
            'uploads': '20/day'
        }
    }

    3.2 自定义根据IP限制

    写一个类,继承SimpleRateThrottle,只需要重写get_cache_key

    from rest_framework.throttling import ScopedRateThrottle,SimpleRateThrottle
    
    #继承SimpleRateThrottle
    class MyThrottle(SimpleRateThrottle):
        scope='luffy'
        def get_cache_key(self, request, view):
            return request.META.get('REMOTE_ADDR')   # 返回
        
    # 局部使用,全局使用 
    REST_FRAMEWORK={
        'DEFAULT_THROTTLE_CLASSES': (
            'utils.throttling.MyThrottle',
        ),
        'DEFAULT_THROTTLE_RATES': {
            'luffy': '3/m'  # key要跟类中的scop对应
        },
    }
  • 相关阅读:
    实验6:Mapreduce实例——WordCount
    暑期生活10
    暑期生活9
    暑期生活8
    暑期生活7
    暑期生活6
    暑期生活5
    暑期生活4
    暑期生活3
    暑期生活2
  • 原文地址:https://www.cnblogs.com/bk134/p/13306274.html
Copyright © 2011-2022 走看看