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

Posted ddzc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了框架之认证接口的源码分析及自定义接口的使用相关的知识,希望对你有一定的参考价值。

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")

以上是关于框架之认证接口的源码分析及自定义接口的使用的主要内容,如果未能解决你的问题,请参考以下文章

mybatis源码分析之04Mapper接口的动态代理

drf之框架基础

drf框架与postman初始

Java Serializable接口(序列化)理解及自定义序列化

0013JDK源码分析之枚举

Junit 3.8.1 源码分析之两个接口