框架之认证接口的源码分析及自定义接口的使用

目录

rest_framework框架之认证的使用和源码实现流程

一、认证功能的源码流程

  • (一)、创建视图函数

    Note
    创建视图函数后,前端发起请求,url分配路由,执行视图类,视图类中执行对应方法必须经过dispatch()即调度方法

    from rest_framework.views import APIView
    from django.shortcuts import HttpResponse
    import json

    class DogView(APIView):
    def get(self, request, *args, **kwargs):
    result = {
    ‘code‘: ‘10000‘,
    ‘msg‘: ‘数据创建成功‘
    }
    return HttpResponse(json.dumps(result))

    def post(self, request, *args, **kwargs):
          return HttpResponse('创建一条订单')
    
      def put(self, request, *args, **kwargs):
          return HttpResponse('更新一条订单')
    
      def delete(self, request, *args, **kwargs):
          return HttpResponse('删除一条订单')
  • (二)、运行dispatch方法

    Note
    如果自己定义了dispatch方法,则程序运行自定义方法,如果没有,程序运行源码中的dispatch方法。从dispatch方法中可以找到原生request在作为参数传递后被initialize_request()函数进行了加工,通过加工的request获得的值包括原生的request和BaseAuthentication实例化对象,所以我们需要找到initialize_request()。

    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 = self.initialize_request(request, *args, **kwargs)
    ‘‘‘
    对原生的request进行加工,获得到的request已经不是原来的request,还包括了其他的参数,
    可以通过新的request获取到内部包含的参数
    加工后的request : Restquest(
    request,
    parsers=self.get_parsers(),
    authenticators=self.get_authenticators(),

    negotiator=self.get_content_negotiator(),
          parser_context=parser_context
      ))
      '''
      self.request = request
      self.headers = self.default_response_headers  # deprecate?
    
      try:
          self.initial(request, *args, **kwargs)
          # 把加工后的request当作参数传递给了initial()函数
          # 需要把在这里查找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
  • 查看initialize_request()函数

    Note
    在initialize_request()函数中返回了authenticators, 通过观察可以看出,authenticators的值来自于另外一个函数get_authenticators()。

    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(),
          authenticators=self.get_authenticators(),
          # authenticators获取到的是实例化后的类对象列表,即[Foo(), Bar()]
          negotiator=self.get_content_negotiator(),
          parser_context=parser_context
      )
  • 找到函数self.get_authenticators()

    Note
    这个函数中实质上是把一个认证类列表实例化为对象列表进行返回,这里就可以得出在上一个驶入函数中的authenticators是一个实例化对象列表。需要继续往源头找,查找authentication_classes

    def get_authenticators(self):
    """
    Instantiates and returns the list of authenticators that this view can use.
    """
    # 例如self.authentication_classes = [foo, bar]
    return [auth() for auth in self.authentication_classes]
    # 列表生成式,auth获取到的是列表中的类,auth()是把获取到的类对象进行实例化操作

  • 查找authentication_classes类

    Note
    在自己编写的代码中并没有定义authentication_classes类,所以程序会从继承的类中去查找,视图类继承自APIView,所以在APIView中找到类authentication_classes。

    class APIView(View):

    # The following policies may be set at either globally, or per-view.
      renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
      parser_classes = api_settings.DEFAULT_PARSER_CLASSES
      authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
      # 继承自APIView中的api_settings.DEFAULT_AUTHENTICATION_CLASSES类
      throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
      permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
      content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
      metadata_class = api_settings.DEFAULT_METADATA_CLASS
      versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

    Summary
    从上述的逻辑可以看出最终要执行的是AUTHENTICATION_CLASSES,所有的程序中都是如果有自定义程序会覆盖掉框架封装好的,没有自定义,程序才会执行封装好的代码。AUTHENTICATION_CLASSES类是这个逻辑中最重要的一环。

  • 上边的逻辑查找到了最基本的Authentication_classes,并且得到加工后的request包含两部分内容:原生的request、Authentication_classes实例化后得到的对象列表,此时需要继续执行dispatch(),执行到try语句时,加工后的request作为参数传递给initial()函数,并执行该函数,此时需要到request.py中查找initial()函数。

    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
    
          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类中查找到request被传递进行,原生的参数在调用的时候格式为:request._request, 加工后的直接是request.属性

    class Request:
    """
    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
          # 加工后的request被作为参数传递,那么传递后相对于本类即为原生的request。
          self.parsers = parsers or ()
          self.authenticators = authenticators or ()
          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
  • 如果进行认证,必须通过user,此时需要查找user程序是否存在,在Request类中找到了user函数,user()方法执行了_authenticate(),查找_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()
                  # 执行_authenticate()
          return self._user
  • 查找_authenticate(),在_authenticate()方法中查找到Authenticator_classes生成的实例化列表类对象,循环的对象具有authenticate()属性/方法,可以直接调用,并通过条件语句判断,如果登陆返回元组,如果没有登陆返回错误提示。此时基本的逻辑已经梳理完成。

    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)
              except exceptions.APIException:
                  raise self._not_authenticated()
    
    
              if user_auth_tuple is not None:
                  self._authenticator = authenticator
                  self.user, self.auth = user_auth_tuple
                  return self._not_authenticated()

二、自定义认证类

通过上述逻辑的整体分析,我们可以编写一个自定义的认证类供视图函数来调用

from rest_framework import exceptions
    # 创建自定义的认证类
    class MyAuthentication(object):
        def authenticate(self, request):
            token = request._request.GET.get('token')
            # 当前传递的参数request是加工过的,需要通过原生的request(获取方法: _request)获取token信息
            if not token:
            # 如果不存在抛出异常
                raise exceptions.AuthenticationFailed("认证失败")
            # 存在返回元组
            return ('alax', None)
            
        def authenticate(self, val):
            pass
    
        class DogView(APIView):
        '''
         需要认证的视图类直接通过authentication_classes=[类名,]的方式来使用
         '''
            authentication_classes = [MyAuthentication]
            def get(self, reqeust, *args, **kwargs):
                result = {
                    'code': '10000',
                    'msg': '获取到所有的数据'
                }
                return HttpResponse(json.dumps(result))
                
            def post(self, request, *args, **kwargs):
                return HttpResponse("创建Dog")
            
            def put(self, request, *args, **kwargs):
                return HttpResponse("更新Dog")
            
            def delete(self, request, *args, **kwargs):
                return HttpResponse("删除Dog")

相关推荐