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 = [ ]
相關文章
相關標籤/搜索