zoukankan      html  css  js  c++  java
  • drf 认证功能

    drf(django rest-framework)认证组件

    复习

    HyperlinkedIdentityField
    ​```python
    功能:快速生成连接
    1. publish = serializers.HyperlinkedIdentityField(view_name='ttt', lookup_field='publish_id', lookup_url_kwarg='pkk')
    view_name:路由的别名,look_up_field:根据表的哪个字段,来拼路径
    lookup_url_kwarg:反向解析有名分组的名字   
    2.写路由:url(r'^publish/(?P<pk>d+)',views.Publish.as_view(),name="ttt")
    3.实例化序列化类的时候,需要把request对象传过去
    book_ser = BookSerializer(ret,many=True,context={'request':request})
    

    数据校验

    生成序列化类对象的时候,把要校验的数据(字典:前端传过来)传过来
    -ser = BookSerializer(data=request.data)
    -ser.is_valid()
    -error_messages
    

    认证简介

    什么是验证
    认证是否登陆
    
    为什么要有认证
    有些操作需登陆后才能访问
    
    如何用认证:
    也就是在使用功能前,验证其是否是登陆状态
    

    属性,方法的查找顺序

    对象>>>>类>>>>>父类
    

    CBV知识点

    一.路由层:
    url(r'^books/$', views.Books.as_view()),
    
    二.项目启动时,就会执行Books.as_view()
    #as_view()源码,csrf_exempt局部禁用csrf,也就是说继承了drf的APIView的CBV都是不需要csrf认证的
     @classmethod
        def as_view(cls, **initkwargs):
            ...
            #调用了父类的as_view方法,也就是View的as_view方法
             view = super(APIView, cls).as_view(**initkwargs)
            ...
            return csrf_exempt(view)
    
    #View的as_view方法,闭包函数,返回了view的内存地址
     @classonlymethod
        def as_view(cls, **initkwargs):
       		...
            def view(request, *args, **kwargs):
                self = cls(**initkwargs)
                if hasattr(self, 'get') and not hasattr(self, 'head'):
                    self.head = self.get
                self.request = request
                self.args = args
                self.kwargs = kwargs
                return self.dispatch(request, *args, **kwargs)
            ...
            return view
    三.所以项目启动时路由层的
    url(r'^books/$', views.Books.as_view())相当于url(r'^books/$', views.Books.view)
    
    四.当有请求过来时,系统调用Books.view方法
    def view(request, *args, **kwargs):
        ....
        #view方法返回了dispatch方法,本质了调用了dispatch方法
        return self.dispatch(request, *args, **kwargs)
    
    五.drf的dispatch源码
        def dispatch(self, request, *args, **kwargs):
            ...
            '''
            self.initialize_request(request, *args, **kwargs)封装了原生request请求
            返回了新的request对象,以前的request.GET 相当于新对象的request.query_params
           
              def query_params(self):
    			...
            	return self._request.GET
           
            新的request重写了__getattr__(当.方法调用属性且当属性不存在时自动触发)
            def __getattr__(self, attr):
                try:
                    return getattr(self._request, attr)
                except AttributeError:
                    return self.__getattribute__(attr)
                request = self.initialize_request(request, *args, **kwargs)
                self.request = request
    		#所以当我们使用request.GET方法时,新对象没有GET方法,触发了__getattr__方法,执行到语句getattr(self._request, attr)获得原request对象的GET属性
    		'''
            try:
                #initial内部进行了认证,权限,频率检验
                self.initial(request, *args, **kwargs)
                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
    

    总结

    drf的APIView与django View的相同点
    本质都是执行了dispatch方法
    
    drf的APIView与django View的不同点
    drf的APIView继承了View
    派生了自己的dispatch方法
    在方法中对原生request对象进行了封装
    
    总结:CBV的本质就是执行了父类的dispatch方法
        dispatch方法中会检索我们自己的CBV是否有get,post等方法
        检索到了就会执行
        APIView 的dispatch方法在检测之前会执行self.initial(request, *args, **kwargs)方法
        此方法内部进行了认证,频率,权限控制 
        dispatch相当于我们学的装饰器,在执行目标函数之前或之后,可以添加我们需要的功能
    

    认证组件:

    drf的认证组件,本质就是在dispatch方法通过反射getattr()找到我们定义的方法前,增加了认证功能
    #initial源码
    
      def initial(self, request, *args, **kwargs):
      		...
            # Ensure that the incoming request is permitted
            #认证
            self.perform_authentication(request)
            #权限
            self.check_permissions(request)
            #频率
            self.check_throttles(request)
    
    #认证功能源码
    '''
    只执行了request.user,user是私有属性,即方法被装饰成属性
    此处的request已经被封装过了(封装函数在认证函数之前)
       def dispatch(self, request, *args, **kwargs):
     		...
     		#封装函数
            request = self.initialize_request(request, *args, **kwargs)
            ...
            try:
            	#认证函数
                self.initial(request, *args, **kwargs)
    '''
      def perform_authentication(self, request):
            request.user
     
    #request.user
     @property
        def user(self):
      		...
            #当我们认证通过返回了user_auth_tuple
            #self.user, self.auth = user_auth_tuple会触发user的set方法
            '''
             @user.setter
            def user(self, value):
                #request有了_user属性,再调用request.user时就会直接返回request._user
                self._user = value
                self._request.user = value
            '''
            #有了_user的属性,直接返回self._user
            if not hasattr(self, '_user'):
                with wrap_attributeerrors():
              		#self是request对象,执行了request的_authenticate方法
                    self._authenticate()
            return self._user
          
    #self._authenticate(),self是request对象
    def _authenticate(self):
        	#self.authenticators 是request对象初始化的属性
            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

       
     #Request的__init__函数       
     class Request(object):
        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.authenticators = authenticators or ()
            ...
     #何时是实例化>>>封装request请求时实例化了Request类
     def initialize_request(self, request, *args, **kwargs):
            parser_context = self.get_parser_context(request)
    		#实例化了 Request, authenticators=self.get_authenticators()
            return Request(
              	...
                #self为APIView对象
                authenticators=self.get_authenticators(),
                ...
            )
        
    #APIView的get_authenticators()方法
        def get_authenticators(self):
            #self为APIView对象,对象的self.authentication_classes属性
            #authenticators为列表,里面放着对象
            return [auth() for auth in self.authentication_classes]
        
    #APIView的authentication_classes属性
    class APIView(View):
        ...
        #自己类没有设置的话从api_settings配置文件中找
        authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
     
    #settings文件,里面俩个没啥用,需要我们自定义authentication_classes,我们自定了话,按照查找顺序,会先从我们的CBV内找
    DEFAULT_AUTHENTICATION_CLASSES
    Default:
    (
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication'
    )
    
    
    综上:drf提供的authentication_classes无法完成我们的验证需求
        我们需要自定义authentication_classes里面存放我们的校验类
        1.authentication_classes=[Auth,]
        
        2.get_authenticators(self)方法返回的是我们验证类的对象的列表
        def get_authenticators(self):
            #self为APIView对象,对象的self.authentication_classes属性
            #authenticators为列表,里面放着对象
            return [auth() for auth in self.authentication_classes]
       3.authenticators=self.get_authenticators()
    	所以authenticators就是验证类对象的列表
    	def initialize_request(self, request, *args, **kwargs):
            parser_context = self.get_parser_context(request)
    		#实例化了 Request, authenticators=self.get_authenticators()
            return Request(
              	...
                #self为APIView对象
                authenticators=self.get_authenticators(),
                ...
            )
        
        4.self.authenticators = authenticators or ()
        所以self.authenticators验证类对象的列表
      #Request的__init__函数       
     class Request(object):
        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.authenticators = authenticators or ()
            ...
        
        5.self.authenticators验证类对象的列表
        所以authenticator为验证类的对象
        验证类对象执行authenticate方法
        
        #self._authenticate(),self是request对象
    	def _authenticate(self):
        	#self.authenticators 是验证类对象的列表,authenticator为验证类对象
            for authenticator in self.authenticators:
                try:
                    #验证类对象执行authenticate()方法,验证通过返回一个元组
                    #self为request对象,这里的参数为request对象
                    #我们定义自己的authenticate方法时有两个参数,一个是self,一个是request
                    user_auth_tuple = authenticator.authenticate(self)
                    #验证不通过抛出异常
                except exceptions.APIException:
                    self._not_authenticated()
                    raise
                #如果不为空,则列表类定义的之后的认证类都不会执行了,因为循环被return打断了
                if user_auth_tuple is not None:
                    self._authenticator = authenticator
                    #self.user,self.auth解压赋值,user是私有属性,也就是方法被伪装为属性,实际调用了user的set方法,如果将当前用户返回给self.user,以后使用request._request.user就可以调用到当前用户对象了
                     '''
                        @user.setter
                        def user(self, value):
                            self._user = value
                            self._request.user = value
                     '''
                     self.user, self.auth = user_auth_tuple
                     return
                 self._not_authenticated()
    
    

    drf认证功能的使用

    1.在我们需要验证的类增加属性authentication_classes = [Auth,]
    class Books(List, APIView):
        authentication_classes = [Auth,]
    2.定义我们的认证Auth类
    from token_redis.models import User
    from rest_framework.exceptions import ValidationError
    from rest_framework.authentication import BaseAuthentication
    class Auth(BaseAuthentication):
        #必须定义authenticate方法和传入参数request
        def authenticate(self,request):
            token = request.query_params.get('token')
            id = request.query_params.get('id')
            if token:
                ret = check_token(token,id)
                if ret:
                    user = User.objects.filter(pk=id).first()
             
                    return user,True
            raise ValidationError('未登陆')
    
    局部使用
    在视图类中加一行:
    authentication_classes = [Auth, ]
    			
    全局使用
    在setting中配置
    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.myauth.Auth",]
    }
    局部禁用
    在视图类中加一行:
    authentication_classes = [ ]
        
    
  • 相关阅读:
    HTML-标题
    HTML-属性
    HTML-元素
    前端 vue Request Payload VS Form Data
    JWT,Session、Cookie(httpOnly,secure) 登录认证
    js 定位当前城市(ip,省份,城市,邮编)接口定位(搜狐、新浪、百度、腾讯API)
    sqlsugar 使用汇总
    asp.net 跨域
    asp.net core程序发布(Webapi,web网站)
    .net 多线程发展1.0--4.0
  • 原文地址:https://www.cnblogs.com/robert-zhou/p/10607369.html
Copyright © 2011-2022 走看看