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 = [ ]
相关文章
相关标签/搜索