rest_framework 登录认证、权限认证、频率控制源码解读

登录认证

# rest_framework 写视图类需要继承 APIView

from rest_framework.views import APIView

# 在APIView中重写了 dispatch 方法
# 1.重新封装了request对象
# 2.加入了登录认证、权限校验、频率控制,在initial方法中

# 登录认证、权限校验、频率控制
self.initial(request, *args, **kwargs)

# 这里传入的request都是APIView重新封装后的request,不是Django原生的request
self.perform_authentication(request) # 登录认证
self.check_permissions(request) # 权限控制
self.check_throttles(request) # 频率控制

# ======================  登录认证 =================

def perform_authentication(self, request):
    """
    Perform authentication on the incoming request.

    Note that if you override this and simply 'pass', then authentication
    will instead be performed lazily, the first time either
    `request.user` or `request.auth` is accessed.
    """
    # 调用了request的user
    request.user 

# 这个request我们需要到 from rest_framework.request import Request 中找
# 找到下面这个方法,用property伪装成了属性

@property
def user(self):
    """
    Returns the user associated with the current request, as authenticated
    by the authentication classes provided to the request.
    """
    # 首先判断是否有_user属性,如果有说明已经通过登录认证,不需要重新认证,但是请求来的时候肯定是没有通过认证的
    if not hasattr(self, '_user'):
        with wrap_attributeerrors(): 
            self._authenticate() # 如果没有通过认证,调用这个方法
    return self._user
    
def _authenticate(self):
    """
    Attempt to authenticate the request using each authentication instance
    in turn.
    """
    # 前面我们说了,这个request是APIView重新封装后的request,其中authenticators属性就是APIView封装进去的,我们去找一下(看下面)
    for authenticator in self.authenticators:
        try:
            # 找到self.authenticators的来源之后,我们发现self.authenticators中放的是一个个类的实例化对象
            # 这里调用了对象的authenticate方法,并且将self传入,self == request对象
            # 所以我们知道,想要自定义一个登录认证类,只要要有authenticate方法,并且接收一个request对象
            user_auth_tuple = authenticator.authenticate(self)
        except exceptions.APIException:
            # 接收异常,也就是说如果认证不通过,我们可以抛出一个异常 APIException,然后结束循环,不进行后续认证
            self._not_authenticated()
            raise
        # 判断authenticate是否有返回值,如果有返回值,必须返回两个值,第一个值赋值给self.user,第二个值赋值给self.auth
        # 在前面我们看到self.user是一个property伪装的属性,所以给self.user赋值会触发@user.setter装饰的方法(看下面)
        if user_auth_tuple is not None:
            self._authenticator = authenticator # 通过认证的对象,权限认证的时候会用到
            self.user, self.auth = user_auth_tuple
            return # 结束循环,认证通过

    self._not_authenticated() # 如果执行到这一步说明所有认证都没有通过
 def _not_authenticated(self):
        """
        Set authenticator, user & authtoken representing an unauthenticated request.

        Defaults are None, AnonymousUser & None.
        """
        self._authenticator = None # 将认证通过类设置为 None,权限认证的时候会用到

        if api_settings.UNAUTHENTICATED_USER: # Django的匿名用户对象
            self.user = api_settings.UNAUTHENTICATED_USER() # 将user赋值为匿名用户
        else:
            self.user = None

        if api_settings.UNAUTHENTICATED_TOKEN:
            self.auth = api_settings.UNAUTHENTICATED_TOKEN()
        else:
            self.auth = None
# ========================= 找APIView封装request的方法
# 知道的APIView=》dispatch方法中的这两句
request = self.initialize_request(request, *args, **kwargs) # 封装新的request
self.request = request # 替换掉原来的request

def initialize_request(self, request, *args, **kwargs):
    """
    Returns the initial request object.
    """
    parser_context = self.get_parser_context(request)

    return Request(
        request, # django原生request
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(), # 这个就是封装登录认证中for authenticator in self.authenticators:用到的属性,在找一下get_authenticators放啊
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )


def get_authenticators(self):
    """
    Instantiates and returns the list of authenticators that this view can use.
    """
    # 这里可以看出,返回的是一个列表,列表中放入了一堆类的实例化对象,我们去找一下authentication_classes
    return [auth() for auth in self.authentication_classes]
    
# APIView在类属性中找到
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES # 从配置文件中获取到了DEFAULT_AUTHENTICATION_CLASSES
# 由上面两句,我们可以知道,有两种方法可以配置自定义登录认证
    # 1.在视图类中写authentication_classes = [class1,class2]
    # 2.在配置文件中配置DEFAULT_AUTHENTICATION_CLASSES
        # 配置文件中不能直接写类,需要写类的导入路径'app名.模块名.类名'
        
# ============= @user.setter装饰的方法

@user.setter
def user(self, value):
    """
    Sets the user on the current request. This is necessary to maintain
    compatibility with django.contrib.auth where the user property is
    set in the login and logout functions.

    Note that we also set the user on Django's underlying `HttpRequest`
    instance, ensuring that it is available to any middleware in the stack.
    """
    # 我们可以看见,将user传入之后,赋值给了self.request._user
    self._user = value
    # 有赋值给了,Django原生request的user
    self._request.user = value

权限控制、频率控制

# 权限认证比登录认证简单
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
def get_permissions(self):
    """
    Instantiates and returns the list of permissions that this view requires.
    """
    return [permission() for permission in self.permission_classes]
    
def check_permissions(self, request):
    """
    Check if the request should be permitted.
    Raises an appropriate exception if the request is not permitted.
    """
    # get_permissions 跟登录认证的一样
    for permission in self.get_permissions():
    # 调用认证类的has_permission方法,如果认证通过返回True,不通过返回False
        if not permission.has_permission(request, self):
            self.permission_denied(
                request,
                message=getattr(permission, 'message', None), # 在认证类中可以写上message 这样就会返回你写的错误信息
                code=getattr(permission, 'code', None)# 返回的状态码
            )
            

# 频率控制

throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES

def get_throttles(self):
    """
    Instantiates and returns the list of throttles that this view uses.
    """
    return [throttle() for throttle in self.throttle_classes]

def check_throttles(self, request):
    """
    Check if request should be throttled.
    Raises an appropriate exception if the request is throttled.
    """
    throttle_durations = []
    # self.get_throttles() 这个跟登录认证、权限认证一样
    for throttle in self.get_throttles():
        # 调用频率认证对象的allow_request方法,如果允许访问返回True,如果访问过快,返回False
        if not throttle.allow_request(request, self):
            #wait方法返回,还需要几秒可以继续访问
            throttle_durations.append(throttle.wait())
    # 如果throttle_durations中有值,说明访问过快
    if throttle_durations:
        # Filter out `None` values which may happen in case of config / rate
        # changes, see #1438
        durations = [
            duration for duration in throttle_durations
            if duration is not None
        ]
        # 取出一个,需要等待的最大值返回
        duration = max(durations, default=None)
        self.throttled(request, duration)
原文地址:https://www.cnblogs.com/wtil/p/13959780.html