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