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'
        )
    }

 

posted @ 2018-06-22 10:22  短毛兔  阅读(417)  评论(0编辑  收藏  举报