DRF认证

作者: 上帝大人 | 来源:发表于2019-09-28 20:14 被阅读0次

    单独看用法

    你定义了一个类CBV,类继承自APIView,在里面赋值authentication_classes字段,字段认证类列表,这几个认证类都需要你自己定义。认证类中要实现authenticate()方法和authenticate_header()方法

    • 认证类一般继承BaseAuthentication类,然后重写authenticate方法
    • authenticate方法需要返回一个元组,第一个对象为用户实例,第二个为Token实例
    class Authentication1():
        def authenticate(self, request):
            token_obj = request._request.GET.get('token')
            if not token_obj:
                raise exception.AuthenticationFailed('用户认证失败')
            return (token_obj.user, token_obj)
         
         def authenticate_header(self, request):
            pass
    ##################################
    from restframework.views import APIView
    class OrderView(APIView):
        authentication_classes = [Authentication1,  Authentication2]
        def get(self, request):
            pass
    

    源码流程

    • 当调用OrderView时,执行dispatch()方法,此方法将request对象做了一个封装,request = self.initialize_request,然后将此视图类进行初始化,最后进行根据字符串进行反射,执行对应的函数。
    • 我们到initialize_request方法中,看到了具体的封装信息,返回的是一个Request类型的对象,里面有原来的request,还加了许多新的属性
            return Request(
                request,
                parsers=self.get_parsers(),
                authenticators=self.get_authenticators(),
                negotiator=self.get_content_negotiator(),
                parser_context=parser_context
            )
    

    我们看到了authenticators = self.get_authenticators()

    • 我们到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]
    
    

    他其实就是返回了一个对象列表,每一个对象都是authentication_classes列表中的认证类的实例。里面的authenticators的值也得到了,现在得到了Request对象。

    • 在dispatch中,得到了Response对象,然后是初始化 视图类 ,执行self.initial(),最后是执行相应的函数。

    在执行初始化操作中,其他的先别看,先看self.perform_authentication(request)方法

            version, scheme = self.determine_version(request, *args, **kwargs)
            request.version, request.versioning_scheme = version, scheme
    
            self.perform_authentication(request)
            self.check_permissions(request)
            self.check_throttles(request)
    

    这个方法就一句,self.user 没有return。所以我们到Request对象中看有没有user属性,request.user执行的结果就是self._authenticate方法,还是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)
                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()
    

    self.authenticators我们已经得到了,是一个认证类实例的列表,遍历列表,并认证类对象的authenticate()方法,返回异常,或者self.user,self.auth = (元组,且必须两个值,),这个就是自定义认证类的返回结果。
    还有就是所有认证都不管,返回默认的self.user,self.auth = (匿名用户,None)

    再看源码,了解全局配置

    • 源码中每次调用的都会先找自己类中有没有 authentication_classes,没有再去全局中找authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES,所以我们可以给全局设置一个值,当自己类中没有时,来全局中找。
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES':[‘app1.utils.auth.Authentication’,‘ app1.utils.auth.Authentication2’ ]
    }
    
    • 这样写,你的继承自APIView的所有的类都会到app1.utils下面的auth.py中找到Authentication和Authentication2进行认证,不想认证的类可以在自己类的内部定义一个空的authtication_classes = [ ] 列表。
      —————————————————————————————————————

    • 其实这就是配置项,让REST_FRAMEWORK的配置项工作,设置其默认的authentication_classes为后面的类。就无需在类中定义了,访问时,用的就是修改后的默认值。

    • 除了DEFAULT_AUTHENTICATION_CLASSES配置,还可以设置其他的。
        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
    

    如果认证失败的话,

            if api_settings.UNAUTHENTICATED_USER:
                self.user = api_settings.UNAUTHENTICATED_USER()
            else:
                self.user = None
    
            if api_settings.UNAUTHENTICATED_TOKEN:
                self.auth = api_settings.UNAUTHENTICATED_TOKEN()
            else:
                self.auth = None
    

    requesr.user = api_settings.UNAUTHENTICATED_USER(),
    request.auth = api_settings.UNAUTHENTICATED_TOKEN()
    所以在配置项中

        'UNAUTHENTICATED_USER':None,
        # 'UNAUTHENTICATED_USER':lambda :"匿名用户",
        'UNAUTHENTICATED_TOKEN':None,
    

    设置未登录的request.user = None,和request.auth = None方便判断。

    为什么要加token进行身份验证呢?

    • 因为源码中APIView的view被装饰器csrf _exempt装饰,此类有csrf验证的豁免权。
      所以我们要把身份验证加上

    restframework中的认证类

    BasicAuthentication,浏览器对你的用户名和密码进行加密,放到header中,在你的header中有加密后的用户名和密码,他会到header中取得加密的值,然后进行解密,得到用户名和密码进行验证。

    梳理

    梳理:

    1. 创建类,继承BaseAuthentication,重写方法(其中第一个必须重写)
      2.1 全局使用
      在settings中添加restframework配置项
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES':[‘app1.utils.auth.Authentication’,‘ app1.utils.auth.Authentication2’ ],
        'UNAUTHENTICATED_USER':None,
        # 'UNAUTHENTICATED_USER':lambda :"匿名用户",
        'UNAUTHENTICATED_TOKEN':None,
    }
    
    1. 2 局部使用/或不使用
      在创建的类中添加静态字段authentications_classses = [‘认证类路径’]或者 = []空列表

    再来一遍源码流程

    dispatch --> 封装了request,获取定义的认证类,然后通过列表生成式创建认证类的对象,在执行类初始化时,会调用类的performe_authencation方法,到request.user,到for循环认证类对象,并调用他的_authenticate()方法进行认证,得到三种结果,认证成功,认证失败,不进行认证。request.user = 用户名或者None或者匿名用户,request.auth = token或者None

    使用代码

    utils.auth.py 认证类

    from rest_framework.authentication import BaseAuthentication
    
    class Authentication(BaseAuthentication):
        '''自定义的认证类'''
        def authenticate(self, request):
            pass # 具体认证逻辑
            '''
            token = request._request.GET.get('token')
            token_obj = model.UserToken.objects.filter(token=token).first()
            
            if not token_obj:
                :raiseexception.AuthenticationFailed('用户认证失败')
            return (token_obj.user, token_obj) # 把元组的元素赋值为request.user 和 request.auth
            '''
        def authenticate_header(self, request):
            pass
    
    

    views.py 视图函数

    from rest_framework.views import APIView
    from utils.auth import Authentication
    from django.http import JsonResponse
    class OrderView(APIView):
        authentication_classes = [Authentication,]  # 局部引用
    
        def get(self,request):
    
            ret = {'code':10000,'msg':None}
            try:
                pass # 具体视图函数逻辑
            except Exception as e:
                pass  # 抛出异常
            return JsonResponse(ret)
    

    settings.py 全局配置

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES':['app1.utils.auth.Authentication',],
    
          'UNAUTHENTICATED_USER':None,  # 默认匿名用户配置
          # 'UNAUTHENTICATED_USER':lambda :"匿名用户",  # 不如上面的好用,直接判断None
    
          'UNAUTHENTICATED_TOKEN':None,  # 默认匿名用户的auth
    }
    

    拓展

    认证信息可以放在header中,request.META可以获取header中的信息
    默认的认证信息为Authoriation,所以获取可以通过加上HTTP_前缀+大写键。

    request.META.get('HTTP_AUTHORIATION')
    

    相关文章

      网友评论

          本文标题:DRF认证

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