zoukankan      html  css  js  c++  java
  • DRF 认证 权限 视图 频率

    认证组件

    使用:写一个认证类,继承BaseAuthentication

      在类中写authenticate方法,把request对象传入

      能从request对象中取出用户携带的token根据token判断是否登录过

      如果登录过,返回两个值 user对象 ,token对象(或者其他自定义的对象)

      如果没有登录过抛异常

    from rest_framework.authentication import BaseAuthentication
    from app01 import models
    from rest_framework.exceptions import AuthenticationFailed
    
    class MyAuth(BaseAuthentication):
        def authenticate(self,request):
            # print('我是认证类中的方法,只要配置了,一定会执行')
            token = request.GET.get('token')
            token_obj=models.Token.objects.filter(token=token).first()
            if token_obj:   #有值表示登录
                # return #可以没有返回值它就直接接续掉这一次循环了 继续下一次循环 如果有值就解压赋值给self.user, self.auth self就是Request对象
                return token_obj.user,token_obj #token_obj.user就是当前登录对象 正向查询  你就可以在任何地方调用当前登录用户了request.user 循环就直接解释了
            else:
                #没有值表示没登录,抛异常
                raise AuthenticationFailed('您没有登录')

     全局使用

      在settings中配置

    # 全局使用认证功能
    REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.MyAuths.MyAuth",]}

    # 这里是固定写法:模块.继承AbstractUser的自定义User表
    AUTH_USER_MODEL = 'user.User'  告诉django使用我们的表名

     

    局部使用

      在视图类中配置

    authentication_classes= [MyAuth,] #认证

    布局禁用 

       在视图类中配置

    authentication_classes= [] 

    update_or_create()有就跟新,没有就创建 查询的时候会查询k=v 有的话就把表中的{'token':token}替换掉 没有就创建一条数据

      参数:key=value,default={'token':token}

     uuid的使用:生成一个唯一的随机字符串

    源码分析:

    APIView中的dispatch--->slef.initial(认证,权限,频率)

    --->self.perform_authentication(认证)

    --->本质又调用新的reqeust对象的user方法

    ---->Request中进入找到user方法内部执行了self._authenticate(注意self是新的request对象)

     

     for循环self.authenticators 对象自己的属性(for authenticator in self.authenticators)

     实例化的时候是在APIView中dispatch中的

    定位一下发现是继承了APIView类中去找get_authenticators方法

    点位一下发现还是APIView中那么就是试图类对象调用这个方法属性查找顺序

    就是我们配置在视图类中的一个认证类 列表中就是一个认证类对象

     那么我们前面的for循环(for authenticator in self.authenticators)就是一个认证类对象

    需要注意的是视图类中使用的认证类的时候如果第一个直接返回值这个for循环就结束了,如果视图类中的认证有多个的话后面的都不会执行了

    权限

    使用写一个权限类

    from rest_framework.permissions import BasePermission
    
    class MyPermision(BasePermission):
        message = '不是超级用户,查看不了'  #错误信息显示中文
        def has_permission(self,request,view):  #reques就是包装好的request,self在APIView类中就是产生的对象 view
            if request.user.user_type == 1:
                return True
            else:return False  #    "detail": "You do not have permission to perform this action."普通用户

    局部使用 

      在视图类中配置:

    permission_classes=[MyPermision,] 

    全局使用 

      在settings中配置

    REST_FRAMEWORK={'DEFAULT_PERMISSION_CLASSES':['自定义的权限类'],}

     局部禁用

      在视图类中配置

    permission_classes=[]

     返回的提示是中文

      权限类中配置   message=中文

    源码分析 APIView---dispatch--initial--check_permissions

    一个个权限类对象

    对象调用自己方法 注释self定位一下发现是APIVeiw类中的那么self就是APIView对象写权限类的has_permission方法的时候传入request  view参数

    APIView调用方法 message就是错误信息抛出中文

    视图

     基本视图

    路由

        url(r'^publish/$', views.PublishView.as_view()),
        url(r'^publish/(?P<pk>d+)/$', views.PublishDetailView.as_view()),

     视图

    class PublishSerializers(serializers.ModelSerializer):
        class Meta:
            model=models.Publish
            fields='__all__'
    
    class PublishView(APIView):
    
        def get(self, request):
            publish_list = models.Publish.objects.all()
            bs = PublishSerializers(publish_list, many=True)
            # 序列化数据
    
            return Response(bs.data)
    
        def post(self, request):
            # 添加一条数据
            print(request.data)
    
            bs=PublishSerializers(data=request.data)
            if bs.is_valid():
                bs.save()  # 生成记录
                return Response(bs.data)
            else:
    
                return Response(bs.errors)
    
    class PublishDetailView(APIView):
        def get(self,request,pk):
            publish_obj=models.Publish.objects.filter(pk=pk).first()
            bs=PublishSerializers(publish_obj,many=False)
            return Response(bs.data)
        def put(self,request,pk):
            publish_obj = models.Publish.objects.filter(pk=pk).first()
    
            bs=PublishSerializers(data=request.data,instance=publish_obj)
            if bs.is_valid():
                bs.save() # update
                return Response(bs.data)
            else:
                return Response(bs.errors)
        def delete(self,request,pk):
            models.Publish.objects.filter(pk=pk).delete()
    
            return Response("")

    第二种视图组件mixin类和generice类编写视图

    路由

     url(r'^publish/$', views.PublishView.as_view({'get': 'list', 'post': 'create'})),
     url(r'^publish/(?P<pk>d+)/$',views.PublishView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),

    视图

    from rest_framework.mixins import CreateModelMixin,RetrieveModelMixin,ListModelMixin,UpdateModelMixin,DestroyModelMixin
    from rest_framework.generics import GenericAPIView
    class PublishView(ListModelMixin,CreateModelMixin,GenericAPIView):
        queryset=models.Publish.objects.all()
        serializer_class=PublishSerializers
    
        def get(self, request):
            return self.list(request)
    
        def post(self, request):
            return self.create(request)
    
    class PublishDetailView(RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin,GenericAPIView):
        queryset=models.Publish.objects.all()
        serializer_class=PublishSerializers
        def get(self,request,*args,**kwargs):
            return self.retrieve(request,*args,**kwargs)
        def put(self,request,*args,**kwargs):
            return self.update(request,*args,**kwargs)
        def delete(self,request,*args,**kwargs):
            return self.destroy(request,*args,**kwargs)

    写ge和post,在其中调用父类的create或者list方法 list是获取多条 

    第三种视图组件使用generics 下ListCreateAPIView,RetrieveUpdateDestroyAPIView

    from rest_framework.generics import ListCreateAPIView,RetrieveUpdateDestroyAPIView
    class PublishView(ListCreateAPIView):
        queryset=models.Publish.objects.all()
        serializer_class=PublishSerializers
    
    class PublishDetailView(RetrieveUpdateDestroyAPIView):
        queryset=models.Publish.objects.all()
        serializer_class=PublishSerializers

    第四种视图组件使用ModelViewSet

    路由

        url(r'^publish/$', views.PublishView.as_view({'get':'list','post':'create'})),
        url(r'^publish/(?P<pk>d+)/$', views.PublishView.as_view({'get':'retrieve','put':'update','delete':'destroy'})),

    视图

    from rest_framework.viewsets import ModelViewSet
    class PublishView(ModelViewSet):
        queryset=models.Publish.objects.all()
        serializer_class=PublishSerializers

    自定义视图方法

    路由

        url(r'^aa/$', views.PublishView.as_view({'get': 'aaa'})),
        url(r'^bb/$', views.PublishView.as_view({'get': 'bbb'})),

    视图

    from rest_framework.viewsets import  ViewSetMixin
    from rest_framework.views import  APIView
    # ViewSetMixin 重写了as_view方法
    class Test(ViewSetMixin,APIView):
    
        def aaa(self,request):
            return Response()

     ViewSetMixin重写了as_view方法,路由配置就改了,可以写成映射的形式{get:get_one}

    as_view方法内部执行效果

      通过反射的取值跟赋值,完成映射,根据请求方式执行相对应的方法(比如:get请求执行get_one方法,在路由中配置{get:get_one})

    频率组件

    使用

      写一个频率类,继承SimpleRateThrottle

      重写get_cache_key,返回self.get_ident(request)

      一定要记住配置一个scop='字符串'

    第二部:在settings中配置

     REST_FRAMEWORK = {
         'DEFAULT_THROTTLE_RATES':{
             'lxx':'3/m'
         }
    }

    局部使用  

      在视图类中配置:

    throttle_classes=[Throttle,]

    全局使用

      在settings中配置 

    REST_FRAMEWORK={'DEFAULT_THROTTLE_CLASSES':['自己定义的频率类'],}

     局部禁用

      在视图类中配置

    throttle_classes=[]

    自定义频率

    #(1)取出访问者ip
    # (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
    # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
    # (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
    # (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败

    代码

    class MyThrottles():
        VISIT_RECORD = {}
        def __init__(self):
            self.history=None
        def allow_request(self,request, view):
            #(1)取出访问者ip
            # print(request.META)
            ip=request.META.get('REMOTE_ADDR')
            import time
            ctime=time.time()
            # (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问
            if ip not in self.VISIT_RECORD:
                self.VISIT_RECORD[ip]=[ctime,]
                return True
            self.history=self.VISIT_RECORD.get(ip)
            # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
            while self.history and ctime-self.history[-1]>60:
                self.history.pop()
            # (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
            # (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
            if len(self.history)<3:
                self.history.insert(0,ctime)
                return True
            else:
                return False
        def wait(self):
            import time
            ctime=time.time()
            return 60-(ctime-self.history[-1])

    源码分析

    def check_throttles(self, request):
            for throttle in self.get_throttles():
                if not throttle.allow_request(request, self):
                    self.throttled(request, throttle.wait())
        def throttled(self, request, wait):
            #抛异常,可以自定义异常,实现错误信息的中文显示
            raise exceptions.Throttled(wait)
    View Code
    class SimpleRateThrottle(BaseThrottle):
        # 咱自己写的放在了全局变量,他的在django的缓存中
        cache = default_cache
        # 获取当前时间,跟咱写的一样
        timer = time.time
        # 做了一个字符串格式化,
        cache_format = 'throttle_%(scope)s_%(ident)s'
        scope = None
        # 从配置文件中取DEFAULT_THROTTLE_RATES,所以咱配置文件中应该配置,否则报错
        THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES
    
        def __init__(self):
            if not getattr(self, 'rate', None):
                # 从配置文件中找出scope配置的名字对应的值,比如咱写的‘3/m’,他取出来
                self.rate = self.get_rate()
            #     解析'3/m',解析成 3      m
            self.num_requests, self.duration = self.parse_rate(self.rate)
        # 这个方法需要重写
        def get_cache_key(self, request, view):
            """
            Should return a unique cache-key which can be used for throttling.
            Must be overridden.
    
            May return `None` if the request should not be throttled.
            """
            raise NotImplementedError('.get_cache_key() must be overridden')
        
        def get_rate(self):
            if not getattr(self, 'scope', None):
                msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
                       self.__class__.__name__)
                raise ImproperlyConfigured(msg)
    
            try:
                # 获取在setting里配置的字典中的之,self.scope是 咱写的luffy
                return self.THROTTLE_RATES[self.scope]
            except KeyError:
                msg = "No default throttle rate set for '%s' scope" % self.scope
                raise ImproperlyConfigured(msg)
        # 解析 3/m这种传参
        def parse_rate(self, rate):
            """
            Given the request rate string, return a two tuple of:
            <allowed number of requests>, <period of time in seconds>
            """
            if rate is None:
                return (None, None)
            num, period = rate.split('/')
            num_requests = int(num)
            # 只取了第一位,也就是 3/mimmmmmmm也是代表一分钟
            duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
            return (num_requests, duration)
        # 逻辑跟咱自定义的相同
        def allow_request(self, request, view):
            """
            Implement the check to see if the request should be throttled.
    
            On success calls `throttle_success`.
            On failure calls `throttle_failure`.
            """
            if self.rate is None:
                return True
    
            self.key = self.get_cache_key(request, view)
            if self.key is None:
                return True
    
            self.history = self.cache.get(self.key, [])
            self.now = self.timer()
    
            # Drop any requests from the history which have now passed the
            # throttle duration
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
            return self.throttle_success()
        # 成功返回true,并且插入到缓存中
        def throttle_success(self):
            """
            Inserts the current request's timestamp along with the key
            into the cache.
            """
            self.history.insert(0, self.now)
            self.cache.set(self.key, self.history, self.duration)
            return True
        # 失败返回false
        def throttle_failure(self):
            """
            Called when a request to the API has failed due to throttling.
            """
            return False
    
        def wait(self):
            """
            Returns the recommended next request time in seconds.
            """
            if self.history:
                remaining_duration = self.duration - (self.now - self.history[-1])
            else:
                remaining_duration = self.duration
    
            available_requests = self.num_requests - len(self.history) + 1
            if available_requests <= 0:
                return None
    
            return remaining_duration / float(available_requests)
    
    SimpleRateThrottle源码分析
    SimpleRateThrottle源码分析
  • 相关阅读:
    PHP 周转换为日期(最后一天) date("o-W")转Y-m-d日期——贴上代码
    开机后apache假死问题解决
    JS控制滚动条 —— 进度条的简单制作
    2014年初组装的第一台电脑
    开启mysql远程连接
    PHP数组的指针操作方法
    Ubuntu Server 13.04安装图形界面
    utf-8和GBK中文字符的长度计算
    2014校园招聘——历程2
    基于LLVM开发属于自己Xcode的Clang插件
  • 原文地址:https://www.cnblogs.com/lakei/p/11135367.html
Copyright © 2011-2022 走看看