zoukankan      html  css  js  c++  java
  • 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.到此就是验证流程整个过程   

    万般皆下品,唯有读书高!
  • 相关阅读:
    对象排序
    MYSQL连不上
    JS读取.properties文件的方法
    Jquery简单使用
    JSP中request获取值
    MYSQL修改字段
    配置文件读取
    spring注解
    Route@简单应用
    MVC客户端验证的小示例
  • 原文地址:https://www.cnblogs.com/s686zhou/p/11720806.html
Copyright © 2011-2022 走看看