Django rest framework认证流程源代码分析

DRF认证源码分析图

DRF认证源码流程图.png

1. CBV的实现原理

通过as_view方法,执行dispatch函数,在dispatch函数中通过request的method方法,调用视图类的不同函数
as_view方法, 执行dispatch方法

    @classonlymethod
    def as_view(cls, **initkwargs):
        """Main entry point for a request-response process."""
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs) ::# 执行dispatch方法::
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view

dispatch方法
判断request请求的method方法,根据method反射,执行类视图中对应的函数

    def dispatch(self, request, *args, **kwargs):
        # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.
        if request.method.lower() in self.http_method_names: # 判断request请求的method方法
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed) # 根method执行反射
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)

2. 继承rest framework 的APIView类

APIView继承了Django的View,并且重写了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) # 执行initial方法

            # 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

            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

重写的dispatch方法除了保留分发的功能,同时做了以下几个操作:

  • 封装request
request = self.initialize_request(request, *args, **kwargs)
self.request = request
  • 执行initial方法
self.initial(request, *args, **kwargs) # 注意此时的request是重新封装后的request

2.1 封装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, # 传入原生的request
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(), # 传入执行get_authenticators()方法后的返回值
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

2.1.1 get_authenticators方法

authentication_classes是一个放置认证类的列表,那么get_authenticators方法返回的就是认证类的实例化对象列表,传入到重写的request对象中。

    def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        return [auth() for auth in self.authentication_classes] # authentication_classes是一个列表

2.2 执行initial方法

perform_authentication函数

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.
    version, scheme = self.determine_version(request, *args, **kwargs)
    request.version, request.versioning_scheme = version, scheme

    # Ensure that the incoming request is permitted
    self.perform_authentication(request)  # 执行perform_authentication函数
    self.check_permissions(request)
    self.check_throttles(request)

2.2.1 perform_authentication函数

调用封装后的requestd对象的user

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

2.2.2 request.user

user方法调用了_authenticate方法

@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'):
        with wrap_attributeerrors():
            self._authenticate()
    return self._user
2.2.2.1 _authenticate方法

self.authenticators其中self指的是封装后的request对象

def _authenticate(self):
    """
    Attempt to authenticate the request using each authentication instance
    in turn.
    """
    for authenticator in self.authenticators:
        try:
            user_auth_tuple = authenticator.authenticate(self) #循环执行认证类中的authenticate方法
        except exceptions.APIException:
            self._not_authenticated()
            raise

        if user_auth_tuple is not None:
            self._authenticator = authenticator
            self.user, self.auth = user_auth_tuple
            return

    self._not_authenticated()

看Request类,我们发现self.authenticators指的是重新封装request对象时,传入的认证类对象列表[auth( ) for auth in self.authentication_classes]

那么上面的_authenticate方法就是对认证类对象列表进行循环执行,并且是执行认证类对象中的authenticate方法,将返回值user_auth_tuple复制给self.user和self.auth

class Request(object):
    """
    Wrapper allowing to enhance a standard `HttpRequest` instance.

    Kwargs:
        - request(HttpRequest). The original request instance.
        - parsers_classes(list/tuple). The parsers to use for parsing the
          request content.
        - authentication_classes(list/tuple). The authentications used to try
          authenticating the request's user.
    """

    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        assert isinstance(request, HttpRequest), (
            'The `request` argument must be an instance of '
            '`django.http.HttpRequest`, not `{}.{}`.'
            .format(request.__class__.__module__, request.__class__.__name__)
        )

        self._request = request
        self.parsers = parsers or ()
        self.authenticators = authenticators or ()  # self.authenticators
        self.negotiator = negotiator or self._default_negotiator()
        self.parser_context = parser_context
        self._data = Empty
        self._files = Empty
        self._full_data = Empty
        self._content_type = Empty
        self._stream = Empty

本文是根据老男孩IT教育的wupeiqi老师的Django rest framework源码分析视频理解整理而成

你可能感兴趣的:(Django rest framework认证流程源代码分析)