美文网首页
Django rest framework认证流程源代码分析

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

作者: 大路仔 | 来源:发表于2019-05-04 20:32 被阅读0次

    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认证流程源代码分析

          本文链接:https://www.haomeiwen.com/subject/kzpfoqtx.html