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