• django Rest Framework----认证/访问权限控制/访问频率限制 执行流程 Authentication/Permissions/Throttling 源码分析


    url:

    url(r'books/$',views.BookView.as_view({'get':'list','post':'create'})) 为例

    当django启动的时候,会调用执行view.BookView.as_view()方法,拿到as_view的返回值view

    url(r'books/$',View.view)

    当用户访问books/的时候,会执行View.view(). 执行APIView.dispatch()

    当用户访问books/时,django拿到request,然后对request进行了重新封装
    并且进行用户认证
    url(r'books/$',View.view) --->  执行 self.dispatch()

    APIView.dispatch()

        def dispatch(self, request, *args, **kwargs):
           ...
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            try:
                self.initial(request, *args, **kwargs)
            ....

     request = self.initialize_request(request, *args, **kwargs)

    def initialize_request(self, request, *args, **kwargs):
            #返回一个Request对象
            return Request(
                ...
                authenticators=self.get_authenticators(),
                ...
            )    
    
    
    
    
    
    def get_authenticators(self):
             # 循环视图类authentication_classes
             # 使用列表推导式,生成每一个实例对象
            return [auth() for auth in self.authentication_classes]

    回到dispatch()中,继续向下执行self.inital(request,*args,**kwags)

    def initial(self, request, *args, **kwargs):
              ...
            # 认证
            self.perform_authentication(request)
            # 权限
            self.check_permissions(request)
            # 访问频率限制
            self.check_throttles(request)

    对于用户认证,我们主要看 self.perform_authentication(request) 干了什么

    def perform_authentication(self, request):
            # 调用了request.user    属性方法
            request.user
    @property
    def user(self):
    
            if not hasattr(self, '_user'):
                with wrap_attributeerrors():
                    # 调用  _authenticate  此时的self是request对象
                    self._authenticate()
            return self._user
    
    
    def _authenticate(self):
                # 此时的self是request ,authenticator每一个验证类对象
                for authenticator in self.authenticators:
                    try:
                        # 使用authenticator调用它的authenticate() 返回一个元组
                        #元组的第一个值是request.user,第二个值是request.auth
                        user_auth_tuple = authenticator.authenticate(self)
                    except exceptions.APIException:
                        # 如果没有验证通过,抛出异常
                        self._not_authenticated()
                        raise
    
                    # 如果 user_auth_tuple  不为空的话,证明authenticator.authenticate(self) 返回了数据
                    #  **** 有多个验证类的话,最后一个认证类返回值,其他验证类不要返回
                    # 否则,这个验证类之后的所有验证类都不会被执行
                    # 如果返回值不为None,赋值后 将会跳出循环
                    if user_auth_tuple is not None:
                        self._authenticator = authenticator
                        # 进行赋值
                        self.user, self.auth = user_auth_tuple
                        # 跳出for循环, 验证通过
                        return
    
                self._not_authenticated()

    认证执行流程:

    1. 查看settings.py中是否配置了作用于全局的REST_FRAMEWORK配置,如果有,就是用全局

    2. 如果settings.py没有配置,就去视图类中(本例是BookView)查看是否配置了authentication_classes

    3. 以上都没有配置,就是用默认的,在APIView中配置

    自定义一个认证

    class BookAuthen():
        def authenticate(self,request):
            #获取token,检验token是否正确,如果正确则,验证通过
            #否则抛出异常
            token_val = request.GET.get('token')
            user_obj = models.User.objects.filter(token__token=token_val).first()
            if user_obj:
                return user_obj.name,token_val
            else:
                raise exceptions.AuthenticationFailed
        def authenticate_header(self,request):
            pass
    
    class BookView(ModelViewSet):
        # 如果提供,就执行BookAuthen,如果没有提供,使用APIView提供的
        authentication_classes = [BookAuthen,]
        queryset = models.Book.objects.all()
        serializer_class = BookModelSerializer

    全局配置(settings.py):

    REST_FRAMEWORK= {
            'DEFAULT_AUTHENTICATION_CLASSES': (
                'bookmanage.auth.authen.BookAuthen',
                'rest_framework.authentication.SessionAuthentication',
                'rest_framework.authentication.BasicAuthentication'
            )
        }

    默认配置(APIView):

    class APIView(View):
            ...
            # 首先api_settings没有DEFAULT_AUTHENTICATION_CLASSES  所以会调用apisetting.__getattr__()
            authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
            ...

    二、访问频率限制

    访问频率限制和用户认证流程几乎相同 

    首先看url

    url(r'books/$',views.BookView.as_view({'get':'list','post':'create'})) 为例
    
    django启动时
    去执行BookView.as_view()方法
    --->APIView.as_view()-->最终返回的是APIView.view url(r'books/$',views.BookView.as_view({'get':'list','post':'create'})) url(r'books/$',APIView.view)

    用户访问时
        执行APIView.view(),其中它调用了父类的as_view(),实际上什么都没做
    执行self.dispatch(),执行的是APIView.dispatch()

    self.initial(request, *args, **kwargs)
    调用了 --> self.check_throttles(request) #此时的self是BookView视图类

    dispatch()中,self.inital(request,*args,**kwags)  -->  self.check_throttles(request)

    def get_throttles(self):
         """
          遍历视图类中的throttle_classes
           拿到每一个自定义访问频率限制类的实例对象
          """
          return [throttle() for throttle in self.throttle_classes]
    
    
     def check_throttles(self, request):
        for throttle in self.get_throttles():
             # 拿到自定义访问频率限制类的实例对象,调用allow_request()
    
              if not throttle.allow_request(request, self):
                     # 如果没有通过验证,执行self.throttled()抛出异常
                      self.throttled(request, throttle.wait())

    自定义访问频率限制类

    # 限制IP频繁访问
        class VisitThrottle(object):
            detail = "访问频率不正常"
            def allow_request(self,request, obj):
                now_time = str(time.time())
                if request.session.get('visittime'):
                    visited = request.session.get('visittime')
                    visited.append(now_time)
                    request.session['visittime'] = visited
                    if len(visited) >= 20:
                        first = visited[-2]
                        last = visited[-1]
                        time_ = time.gmtime(float(last) - float(first))
                        print(time_)
                        if time_.tm_min > 1:
                            request.session['visittime'] = []
                            return True
                        elif time_.tm_min <= 1:
                            raise exceptions.Throttled(detail=self.detail)
    
                else:
                    request.session['visittime'] = [now_time,]
                print(request.META.get('REMOTE_ADDR'))
    
    
                return True
    
        class BookView(ModelViewSet):
            #在视图类中配置自定义频率访问组件
            throttle_classes = [VisitThrottle]
            queryset = models.Book.objects.all()
            serializer_class = BookModelSerializer

    默认访问频率限制

    由于每一个视图类都是继承自APIView,
    如果配置throttle_classes,就使用自定义的
    如果没配置throttle_classes,APIView配置了默认的
    class APIView(View):
            ....
    
            # 由于api_settings没有DEFAULT_THROTTLE_CLASSES,会调用api_setting的__getattr__()
            throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    
            ....
    
    
        api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
    
    
        class APISettings(object):
            def __init__(self, user_settings=None, defaults=None, import_strings=None):
                #不会进入
                if user_settings:
                    self._user_settings = self.__check_user_settings(user_settings)
                # 将DEFAULTS 复制给  self.defaults
                self.defaults = defaults or DEFAULTS
    
            @property
            def user_settings(self):
                if not hasattr(self, '_user_settings'):
                    self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})
                return self._user_settings
    
            def __getattr__(self, attr):
                if attr not in self.defaults:
                    raise AttributeError("Invalid API setting: '%s'" % attr)
                try:
                    # self.user_settings : 它从settings.py中取找 是否存在 REST_FRAMEWORK配置项
                    # 如果有,因为他是一个字典,取[DEFAULT_THROTTLE_CLASSES]对应的路径自定义访问频率类
                    val = self.user_settings[attr]
                except KeyError:
    
                    # 如果没有,就使用默认的
                    # self.defaults[DEFAULT_THROTTLE_CLASSES]
                    val = self.defaults[attr]
    
                ....
                return val

    全局配置

    1. 此时,应该将自定义的访问频率组件提取出来单独放在一个py文件中
    2. 指定路径类
    3. 所有的url访问都会进行访问频率限制
     REST_FRAMEWORK= {
            'DEFAULT_AUTHENTICATION_CLASSES': (
                'bookmanage.auth.authen.BookAuthen',
            ),
            'DEFAULT_THROTTLE_CLASSES':(
                'bookmanage.auth.throttle.VisitThrottle'
            )
        }
  • 相关阅读:
    [Linux]
    [Nginx]
    [Nginx]
    [Linux]
    [Linux]
    [Linux]
    [Linux]
    [Linux] -Docker修改空间大小
    [Linux]
    [MySql]
  • 原文地址:https://www.cnblogs.com/weihengblog/p/9212177.html
Copyright © 2020-2023  润新知