欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

drf 认证功能

程序员文章站 2022-07-12 11:20:48
...

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 = [ ]