• drf(二)——认证


    drf认证

    官网地址:https://www.django-rest-framework.org/api-guide/requests/

    1.drf的执行流程与源码剖析

    from rest_framework.views import APIView
    
    class StudentView(APIView):
        def get(self,request,*args,**kwargs):
            pass
    

    image-20220403192456485
    说明:图片上的settings表示内容可能与配置文件有关;

    • dispatch()函数源码:

      # View的入口是dispatch()
      # 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)
          # 将添加后的request复制给原来的request
          self.request = request
          self.headers = self.default_response_headers  # deprecate?
      
          try:
             self.initial(request, *args, **kwargs) #执行功能件下方详解
      
             # 原来View中执行的函数;
             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源码

      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(),# 为原生request封装其他参数;
              negotiator=self.get_content_negotiator(),
              parser_context=parser_context
          )
      
    • 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]
      
    • initial函数源码;

      self.initial(request, *args, **kwargs)
      
      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) #执行权限认证
          self.check_permissions(request)
          self.check_throttles(request)
      
    • perform_authentication源码

      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
      
    • user源码

      @property
      def user(self):
          if not hasattr(self, '_user'):#使用反射,判断对象是否包含该属性
              with wrap_attributeerrors():
                  self._authenticate() # 执行私有函数
          return self._user
      
    • _authenticate函数;

      def _authenticate(self):
          for authenticator in self.authenticators:
              # authenticators=self.get_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 # 将元组的两个元素,传递给对象中的两个变量,可以在视图函数中被调用
                  # 两个变量一般为用户和token
                  return
      
          self._not_authenticated()# 不存在返回值,则执行改函数。
          
          
      '''
      即,在此处循环
      	[auth() for auth in self.authentication_classes]
      这个了列表
      '''
      
      def _not_authenticated(self): #走到此处即为匿名用户。
          self._authenticator = None # 没有认证函数默认为 None
          if api_settings.UNAUTHENTICATED_USER:
              self.user = api_settings.UNAUTHENTICATED_USER() #读取配置文件中的值
          else:
              self.user = None # 否则将值赋值为None
          if api_settings.UNAUTHENTICATED_TOKEN:# 
              self.auth = api_settings.UNAUTHENTICATED_TOKEN() #默认token
          else:
              self.auth = None
      

    2.简单使用(局部)

    说明:通过上述源码流程应该得知,自己定义的认证类,一般需要返回一个元组,包含两个值,通常为用户token;当没有这两项值的时候,即为未登录的状态,而这两项值被封装到当前request对象中,方便自定义函数中使用。

    from django.shortcuts import HttpResponse
    from rest_framework.views import APIView
    from rest_framework.authentication import BaseAuthentication
    from rest_framework import exceptions
    
    def md5(user): #生成token
        import hashlib
        import time
        ctime = str(time.time())
        m = hashlib.md5(bytes(user,encoding='utf-8'))
        m.update(bytes(ctime,encoding='utf-8'))
        return m.hexdigest()
    
    
    class MyAuthentication(object):
        def authenticate(self, request):
            token = request._request.GET.get('token')
            # 获取用户名和密码,去数据校验
            if not token:
                raise exceptions.AuthenticationFailed('用户认证失败')
            return ("alex", None)
    
        def authenticate_header(self, val):
            pass
        
    class StudentView(APIView):
        authentication_classes=[MyAuthentication,] #注册使用验证函数
        def get(self,request,*args,**kwargs):
    		if request.user and request.auth: #当存在用户与token的时候进行返回
                # 此处可以升级为数据库查询的方式,但是简单使用的方式不常用。
            	return HttpResponse("Hello world")
            
    

    3.进阶使用(全局)

    3.1 内置认证类

    通过上述的源码进行分析得知:认证类必须要实现authenticate方法,而框架内部封装了认证类,因此我们编写自己的认证类的时候通常继承框架提供的类。

    # 内置认证类。
    class BaseAuthentication:
        """
        All authentication classes should extend BaseAuthentication.
        """
    
        def authenticate(self, request):
            """
            Authenticate the request and return a two-tuple of (user, token).
            """
            '''继承该类的话,该方法必须被重写,类似于java中的接口,抽象方法'''
            raise NotImplementedError(".authenticate() must be overridden.")
    
        def authenticate_header(self, request):
    		'''
    		不常用。主要用于返回与浏览器结合使用的状态框使用
    		'''
            pass
    

    3.2 全局使用

    # 自定义认证类,规范
    
    from rest_framework.authentication import BaseAuthentication
    from rest_framework import exceptions
    
    from app01 import models
    
    class MyAuthentication(BaseAuthentication):
    
        def authenticate(self, request):
            # 源码中为原生request进行了封装为_request,也可以不写按照继承关系亦可找到
            token=request._request.GET.get('token')
            token_obj = models.UserToken.objects.filter(token=token).first()
            if not token_obj:
                raise exceptions.AuthenticationFailed('用户认证失败')
            # 在rest framework内部会将整个两个字段赋值给request,以供后续操作使用
            return (token_obj.user, token_obj)
    
    
        def authenticate_header(self, request):
            pass
    
    # 视图函数,
    
    import time #导包按照开发规范进行导包。
    import hashlib
    
    from django.http.response import JsonResponse
    from rest_framework.views import APIView
    
    from app01 import models
    
    def md5(user):
        ctime = str(time.time())
        m = hashlib.md5(bytes(user, encoding='utf-8')) # 使用当前时间戳,为算法加盐
        m.update(bytes(ctime, encoding='utf-8'))
        return m.hexdigest()
    
    class AuthView(APIView):
        """
        用于用户登录认证
        """
        authentication_classes = []
        def post(self,request,*args,**kwargs): #登录功能一般使用post进行操作
            ret = {'code':1000,'msg':None} #初始化返回值
            try:
                user = request._request.POST.get('username')
                pwd = request._request.POST.get('password')
                # 往数据库查询参数
                obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
                if not obj:# 用户不存在
                    ret['code'] = 1001
                    ret['msg'] = "用户名或密码错误"
                # 为登录用户创建token
                token = md5(user)
                # 存在就更新,不存在就创建
                models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
                ret['token'] = token
            except Exception as e:
                ret['code'] = 1002
                ret['msg'] = '请求异常'
    
            return JsonResponse(ret)
    
    class StudentView(APIView):
        def get(self,request,*args,**kwargs):
            if request.user and request.auth:
                return JsonResponse({"msg":"查看成功!!!"})
            return JsonResponse({"msg":"无权查看"})
    

    通过最开始的源码,可以进行配置文件的全局使用;

    # APIView的部分源码
    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
        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
    

    匿名用户中用到的配置文件中的值。

    image-20220405183706971

    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":['app01.utils.auth.MyAuthentication',],
        "UNAUTHENTICATED_USER":None, # 匿名,request.user = None
        "UNAUTHENTICATED_TOKEN":None,# 直接使用None方便检查
    }
    

    3.3 使用效果

    • 登录接口:

      image-20220405184023046

      image-20220405184023046

      说明:使用POST请求并没有设置token的参数,但是却没有触发错误;因此可能drf的post请求处理了csrf_token;

    • 查询接口

      image-20220405205614279
      因为视图函数返回的是JsonResponse,并不是drf的序列化组件,因此暂时显示未序列化的值。

      使用runapi进行查看

      image-20220405210304993

    • 认证后期会涉及到JWT,此处不做过多的解释。

    继续努力,终成大器;

  • 相关阅读:
    欧几里得方程 模幂运算 模乘运算 蒙哥马利模乘 素数测试
    HLG 1058workflow解题报告
    poj 3264Balanced Lineup解题报告
    JavaScript之HTMLCollection接口
    随记2(IE下调试Javascript)
    抽象类和接口
    JavaScript之字符串处理函数
    随记1
    多态
    自动内存管理
  • 原文地址:https://www.cnblogs.com/Blogwj123/p/16103951.html
Copyright © 2020-2023  润新知