restframework 认证源码流程

一.请求来到之后,都要先执行dispatch方法,根据请求方式不同触发get/post/put/delete等方法

注意:APIView中的dispatch方法有很多的功能

    def dispatch(self, request, *args, **kwargs):
        """
        `.dispatch()` is pretty much the same as Django's regular dispatch,
        but with extra hooks for startup, finalize, and exception handling.
        """
        self.args = args
        self.kwargs = kwargs
        第一步:对request进行加工(添加数据)
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request
        self.headers = self.default_response_headers  # deprecate?

        try:
            #第二步:
                #处理版权信息
                #认证
                #权限
                #请求用户进行访问频率的限制
            self.initial(request, *args, **kwargs)

            # Get the appropriate handler method
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed

            # 第三步、执行:get/post/put/delete函数
            response = handler(request, *args, **kwargs)

        except Exception as exc:
            response = self.handle_exception(exc)

        #第四步、 对返回结果再次进行加工
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response

二.接下来  我们从第一步慢慢入手 ,分析具体是对request添加了哪些数据

1.首先request = self.initialize_request(request, *args, **kwargs)点进去,你会发现:Request多增加了四个数据

 def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        #吧请求弄成一个字典返回了
        parser_context = self.get_parser_context(request)

        return Request(
            request,
            parsers=self.get_parsers(),  #解析数据,默认的有三种方式,可点进去看
            #self.get_authenticator优先找自己的,没有就找父类的
            authenticators=self.get_authenticators(), #获取认证相关的所有类并实例化,传入request对象供Request使用
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

2.获取认证相关的具体的类列表   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]  #[SessionAuthentication,BaseAuthentication]

3.查看认证的类:self.authentication_classes

authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES  #默认的,如果自己有会优先执行自己的

4.接着我们走进api_settings中看看这些系统的认证器

DEFAULTS = {
    # Base API policies
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',   #这时候就找到了他默认认证的类了,可以导入看看
        'rest_framework.authentication.BasicAuthentication'
    ),

5.我们导入类看看里面具体做了什么事情  上述两个SessionAuthentication和BasicAuthentication都是继承了BaseAuthentication

from rest_framework.authentication import SessionAuthentication
from rest_framework.authentication import BaseAuthentication

6.里面有两个方法分别是  authenticate()方法  和 authenticate_header()方法

class BaseAuthentication(object):
    """
    All authentication classes should extend BaseAuthentication.
    """

    def authenticate(self, request):
        """
        Authenticate the request and return a two-tuple of (user, token).
        """
        raise NotImplementedError(".authenticate() must be overridden.")

    def authenticate_header(self, request):
        """
        Return a string to be used as the value of the `WWW-Authenticate`
        header in a `401 Unauthenticated` response, or `None` if the
        authentication scheme should return `403 Permission Denied` responses.
        """
        pass

具体认证处理,那我们就看一看BasicAuthentication  从headers里面获取用户名和密码

class BasicAuthentication(BaseAuthentication):
    """
    HTTP Basic authentication against username/password.
    """
    www_authenticate_realm = 'api'

    def authenticate(self, request):
        """
        Returns a `User` if a correct username and password have been supplied
        using HTTP Basic authentication.  Otherwise returns `None`.
        """
        auth = get_authorization_header(request).split()

        if not auth or auth[0].lower() != b'basic':
            return None   #返回none不处理。让下一个处理

        if len(auth) == 1:
            msg = _('Invalid basic header. No credentials provided.')
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = _('Invalid basic header. Credentials string should not contain spaces.')
            raise exceptions.AuthenticationFailed(msg)

        try:
            auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')   #用partition切割冒号也包括
        except (TypeError, UnicodeDecodeError, binascii.Error):
            msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
            raise exceptions.AuthenticationFailed(msg)

        userid, password = auth_parts[0], auth_parts[2]  # 返回用户和密码
        return self.authenticate_credentials(userid, password, request)

    def authenticate_credentials(self, userid, password, request=None):
        """
        Authenticate the userid and password against username and password
        with optional request for context.
        """
        credentials = {
            get_user_model().USERNAME_FIELD: userid,
            'password': password
        }
        user = authenticate(request=request, **credentials)

        if user is None:
            raise exceptions.AuthenticationFailed(_('Invalid username/password.'))

        if not user.is_active:
            raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))

        return (user, None)

    def authenticate_header(self, request):
        return 'Basic realm="%s"' % self.www_authenticate_realm

看上述源码 主要实现的是authenticate()方法   其返回值是一个 元组  return (user,None) user对象 和 None  。因此我们之后也可以模仿他写一个自定义认证类

7.第一步操作主要是将这些认证器变成列表添加进request对象中,具体的认证流程 在 第二步 下面我们回到最初的dispatch 看第二步

 self.initial(request,*args,**kwargs)  这一步主要是 1.处理版本信息 2. 认证  3. 权限  4.请求用户进行访问频率的限制

def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)

        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg

        # Determine the API version, if versioning is in use.
        #2.1 处理版本信息
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme

        # Ensure that the incoming request is permitted
        #2.2 认证
        self.perform_authentication(request)
        # 2.3 权限
        self.check_permissions(request)
        # 2.4 请求用户进行访问频率的限制
        self.check_throttles(request)

8.进来后我们看到了 一行关键的 认证代码  self.perform_authentication(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已经是加工后的request了

9.出乎意料  就他妈一行代码  reqeust.user   你会发现想继续看看user是什么时点也点不进去,很头疼。不要急,我们接下来稍微分析一下

 request是对象   它既然能点出来  而且在没有做任何返回的情况下  我们可以断定 user是一个属性方法  因此我们可以去request类中看看到底有没有一个

   user方法。果然不出所料

@property
    def user(self):
        """
        Returns the user associated with the current request, as authenticated
        by the authentication classes provided to the request.
        """
        if not hasattr(self, '_user'):  #判断有没有user  没有的话就执行下面的 self._authenticate()
            with wrap_attributeerrors():
                self._authenticate()  #
        return self._user  #返回user

10.点进来后 又惊喜的发现 有self._authenticate()方法  开始用户认证

def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        #循环对象列表
        for authenticator in self.authenticators:
            try:
                #执行每一个对象的authenticate 方法
                user_auth_tuple = authenticator.authenticate(self)   
            except exceptions.APIException:
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple  #返回一个元组,user,和auth,赋给了self,
                # 只要实例化Request,就会有一个request对象,就可以request.user,request.auth了
                return

        self._not_authenticated() 

11.这里user_auth_tuple = authenticator.authenticate(self)   开始将之前reqeust对象中的认证器列表循环遍历 并进行验证

  验证成功就会  返回一个元组  并解压赋值给  用户  user   和  用户Token  auth  

  如果认证不成功:执行 self._not_authenticated()方法

def _not_authenticated(self):
        """
        Set authenticator, user & authtoken representing an unauthenticated request.

        Defaults are None, AnonymousUser & None.
        """
        #如果跳过了所有认证,默认用户和Token和使用配置文件进行设置
        self._authenticator = None  #

        if api_settings.UNAUTHENTICATED_USER:
            self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户AnonymousUser
        else:
            self.user = None  # None 表示跳过该认证

        if api_settings.UNAUTHENTICATED_TOKEN:
            self.auth = api_settings.UNAUTHENTICATED_TOKEN()  # 默认值为:None
        else:
            self.auth = None

    # (user, token)
    # 表示验证通过并设置用户名和Token;
    # AuthenticationFailed异常

12.到此就是验证流程整个过程   

万般皆下品,唯有读书高!
原文地址:https://www.cnblogs.com/s686zhou/p/11720806.html