目录
drf之认证、权限、限流、过滤
一 drf认证Authentication
1 drf认证功能介绍
0) 认证,频率,权限
1) 用户是否登录到系统中
2) 后期基本上会用JWT的认证
3) 自定制的认证
2 认证功能源码分析
2.1 源码分析
1) APIView---》dispatch---》self.initial(request, *args, **kwargs)--》self.perform_authentication(request)---》Request.user--->self._authenticate(self):Request类的方法---》self.authenticators:Request类的属性---》在Request对象实例化的时候传入的----》Request在什么时候实例化的?dispatch的时候---》APIView:self.get_authenticators()--》return [auth() for auth in self.authentication_classes]----》如果在自己定义的视图类中写了authentication_classes=[类1,类2]----》Request的self.authenticators就变成了我们配置的一个个类的对象
# # 源码分析详解
# a) APIView---》
@classmethod
def as_view(cls, **initkwargs):
view = super().as_view(**initkwargs)
# 调用view下的as_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)
# b) dispatch---》 # 调用了dispatch,APIView中有此方法,故是APIView中的dispatch方法
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request # 对request进行了属性的赋值
try:
self.initial(request, *args, **kwargs) # 运行位置
# c) self.initial(request, *args, **kwargs)--》
def initial(self, request, *args, **kwargs):
self.perform_authentication(request) # 运行到此处
self.check_permissions(request)
self.check_throttles(request)
# d) self.perform_authentication(request)---》 # 运行
def perform_authentication(self, request):
"""
Perform authentication on the incoming request. Note that if you override this and simply 'pass', then authentication will instead be performed lazily, the first time either
`request.user` or `request.auth` is accessed. """
# 不是原生django的user,是新的Request对象的方法
request.user
# e) Request.user---> # Request下的user方法
@property
def user(self):
""" Returns the user associated with the current request, as authenticated by the authentication classes provided to the request. """
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
# f) self._authenticate(self):Request类的方法---》
def _authenticate(self):
""" Attempt to authenticate the request using each authentication instance in turn. """
for authenticator in self.authenticators: # 是Request类的属性
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()
# g) self.authenticators: Request类的属性---》 h) 在Request对象实例化的时候传入的----》
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._request = request
self.parsers = parsers or ()
self.authenticators = authenticators or ()
# i) Request在什么时候实例化的? dispatch开始的时候的时候---》
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs) # request在APIView的dispatch这里实例化的
# j) APIView:self.get_authenticators()--》
def initialize_request(self, request, *args, **kwargs):
""" Returns the initial request object. """
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
# k) return [auth() for auth in self.authentication_classes]----》
def get_authenticators(self):
""" Instantiates and returns the list of authenticators that this view can use. """
return [auth() for auth in self.authentication_classes] # 一个列表推导式
# l) 如果在自己定义的视图类中写了authentication_classes=[类1,类2]----》
class APIView(View):
# The following policies may be set at either globally, or per-view.
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
# m) Request的self.authenticators就变成了我们配置的一个个类的对象
2.2 Request类的: self._authenticate(self) 方法
2) self._authenticate(self):Request类的方法
def _authenticate(self):
for authenticator in self.authenticators: # BookView中配置的一个个类的对象
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
3) 只要在视图类中配置authentication_classes = [MyAuthen.LoginAuth, ]
就会执行上面的方法,执行认证
3 自定义认证类(重点)
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
# 1 使用(GET方法)
1)-定义一个类,继承BaseAuthentication
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
token = request.GET.get('token')
res = models.UserToken.objects.filter(token=token).first()
if res:
return 元组
else:
raise AuthenticationFailed('您没有登录')
2)-重写authenticate方法
3)-局部使用和全局使用
-局部:在视图类中配置(只要配置了,就是登录以后才能访问,没配置,不用登录就能访问)
authentication_classes = [MyAuthen.LoginAuth, ]
-全局
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.MyAuthen.LoginAuth", ]
}
4)-注意:
1) 认证类,认证通过可以返回一个元组,有两个值,第一个值会给,request.user,第二个值会个request.auth
2) 认证类可以配置多个,按照从前向后的顺序执行,如果前面有返回值,认证就不再继续往下走了
4 认证功能局部使用和全局使用
1) 全局使用(所有接口,都需要登录才能访问)
-在配置文件中
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.MyAuthen.LoginAuth", ]
}
2) 局部使用
-在想局部使用的视图类上
authentication_classes = [MyAuthen.LoginAuth,]
3) 局部禁用
-在想禁用的视图类上
authentication_classes = []
5 内置认证方案(需要配合权限使用)
5.1 全局使用
# 可以在配置文件中配置全局默认的认证方案
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication', # session认证
'rest_framework.authentication.BasicAuthentication', # 基本认证
)
}
5.2 局部使用
# 也可以在每个视图中通过设置authentication_classess属性来设置
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView
class ExampleView(APIView):
# 类属性
authentication_classes = [SessionAuthentication, BasicAuthentication]
...
5.3 认证失败的返回值
# 认证失败会有两种可能的返回值:
-401 Unauthorized # 未认证
-403 Permission Denied # 权限被禁止
二 def权限Permissions
# 权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。
-在执行视图的dispatch()方法前,会先进行视图访问权限的判断
-在通过get_object()获取具体对象时,会进行模型对象访问权限的判断
1 自定义权限功能(重点)
1) 登录成功以后,超级用户可以干某些事,普通用户不能干---》超级用户可以查看某些接口,普通用户不能查看
2) 使用:写一个类继承BasePermission,重写has_permission
class SuperPermission(BasePermission):
def has_permission(self, request, view):
# Return `True` if permission is granted, `False` otherwise.
# 超级用户可以访问,除了超级用户以外,都不能访问
if request.user.user_type == '1':
return True
else:
return False
3) 局部使用和全局使用
-在想局部使用的视图类上
permission_classes = [MyAuthen.SuperPermission]
-全局使用
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": ["app01.MyAuthen.SuperPermission", ]
}
-局部禁用
permission_classes = []
2 权限功能局部使用和全局使用
# 1 使用方式
1)-在想局部使用的视图类上
permission_classes = [MyAuthen.SuperPermission]
2)-全局使用
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": ["app01.MyAuthen.SuperPermission", ]
}
3)-局部禁用
permission_classes = []
3 内置的权限类
# 内置权限类
from rest_framework.permissions import AllowAny,IsAuthenticated,IsAdminUser,IsAuthenticatedOrReadOnly
- AllowAny # 允许所有用户
- IsAuthenticated # 仅通过认证的用户
- IsAdminUser # 仅管理员用户
- IsAuthenticatedOrReadOnly # 已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的只能查看数据。
4 权限源码分析
4.1 源码分析
# 1 源码分析
-APIView的dispatch---》APIView的initial---》APIView的check_permissions(request)
for permission in self.get_permissions(): # 权限类对象放到列表中
if not permission.has_permission(request, self):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
4.2 错误信息的中文显示
# 2 错误信息的中文显示
在权限类中加一个 message=字符串 ————》message=getattr(permission, 'message', None),
5 模型层choice字段使用(重点)
1) 模型表:Student表,写接口应该选择继承哪个视图类
2) 推荐使用自动生成路由的方式(继承ViewSetMixin及它的字类)
3) 但是目前来说,你先实现功能即可(至于选择哪个,慢慢体会)
4) choice的使用
-在模型类中使用
sex = models.SmallIntegerField(choices=((1, '男'), (2, '女'), (3, '未知')), default=1)
-在视图类中,在序列化类中
-get_字段名_dispaly()的方法,该方法获得choice字段对应的数据
三 drf限流Throttling
1 自定义频率类(分析,了解)
1.1 自定义频率类用途及使用
1) 限制某个人,某个ip的访问频次
2) 自定义频率类及使用
from rest_framework.throttling import BaseThrottle
class MyThrottle(BaseThrottle):
VISIT_RECORD = {} # 存用户访问信息的大字典
def __init__(self):
self.history = None
def allow_request(self,request,view):
# 根据ip进行频率限制,每分钟只能访问3次
# 限制的逻辑
'''
#(1)取出访问者ip
#(2)判断当前ip不在访问字典里,添加进去,并且直接返回True, 表示第一次访问,在字典里,继续往下走
#(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
#(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
#(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
'''
# (1)取出访问者ip
# print(request.META)
ip = request.META.get('REMOTE_ADDR')
import time
ctime = time.time()
# (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问
if ip not in self.VISIT_RECORD:
self.VISIT_RECORD[ip] = [ctime, ]
return True
self.history = self.VISIT_RECORD.get(ip)
# (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
while self.history and ctime - self.history[-1] > 60:
self.history.pop()
# (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
# (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
if len(self.history) < 3:
self.history.insert(0, ctime)
return True
else:
return False
def wait(self):
# 还剩多长时间能访问
import time
ctime = time.time()
return 60 - (ctime - self.history[-1])
1.2 局部使用与全局使用
1)-局部使用
throttle_classes = [auth.MyThrottle,]
2)-全局使用
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES':['app01.auth.MyThrottle',],
}
2 内置频率类使用
2.1 内置频率类
-BaseThrottle: # 基类
-AnonRateThrottle: # 限制匿名用户的访问次数
-SimpleRateThrottle:# 我们自定义扩写它
-ScopedRateThrottle:
-UserRateThrottle: # 限制登录用户访问次数
2.2 扩展内置频率类(重点记住)
# 扩展内置频率类(重点记住)
1)-写一个类,继承SimpleRateThrottle
class MySimpleThrottle(SimpleRateThrottle):
scope = 'xxx'
def get_cache_key(self, request, view):
#以ip限制
return self.get_ident(request)
2)-setting.py中配置
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES' : {
'xxx':'10/m' # key跟scope对应,value是一个时间
}
}
3)-局部使用与全局使用
2.3 源码分析
# 源码分析
-继承SimpleRateThrottle---》allow_request(跟咱们写的一样)
2.4 其它内置频率类
# 其它内置频率类
1)-限制未登录用户的频率(AnonRateThrottle)(根据ip限制)
-使用:
-局部使用,全局使用
-setting.py中配置
'DEFAULT_THROTTLE_RATES' : {
'anon':'1/m'
}
2)-限制登录用户访问次数 UserRateThrottle(根据用户id限制)
-使用:
-局部使用,全局使用
-setting.py中配置
'DEFAULT_THROTTLE_RATES' : {
'user':'1/m'
}
3)-ScopedRateThrottle(有兴趣看一下,没有就不看了)
四 过滤Filter与排序
1 内置,第三方过滤功能(次重点)
1) 过滤:筛选查询结果
2) 内置筛选的使用
-在视图类中配置
filter_backends = [SearchFilter,]
search_fields = ('name',) # 表模型中的字段
-查询的时候
http://127.0.0.1:8000/students/?search=e
3) 第三方扩展的过滤功能
-pip3 install django-filter :最新版本(2.4.0)要跟django2.2以上搭配
-在视图类中配置
filter_backends = [DjangoFilterBackend,]
filter_fields = ['name','age']
-查询的时候
http://127.0.0.1:8000/students/?name=lqz&age=18
2 排序功能(次重点)
-在视图类中配置
filter_backends = [OrderingFilter,]
ordering_fields = ['id','age']
-查询的时候
http://127.0.0.1:8000/students/?ordering=-age
### 过滤后再排序
-在视图类中配置
filter_backends = [OrderingFilter,DjangoFilterBackend]
ordering_fields = ('id', 'age')
filter_fields = ['name','age']
-查询的时候
http://127.0.0.1:8000/students/?name=lqz&age=19&ordering=-age,-id
3 自定制过滤器
3.1 使用原理
1)-查询所有才会有过滤---》list才需要过滤---》queryset = self.filter_queryset(self.get_queryset())---》GenericAPIView-->filter_queryset
3.2 使用方法
# 基于django-filter扩写
1) 写一个类MyFilter,继承BaseFilterBackend
2) 重写filter_queryset方法,在该方法内部进行过滤(自己设置的过滤条件)
def filter_queryset(self, queryset):
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
# self.get_queryset() 就是 models.表名.objects.all()
3) 返回queryset对象(过滤后的queryset对象)
4) 配置在视图类中
filter_backends = [MyFilter,]
五 拓展
1) select_related的使用
# 基于对象的跨表查询,可以减少查询数据库的次数
articleList=models.Article.objects.select_related("category").all()
for article_obj in articleList:
# Doesn't hit the database, because article_obj.category
# has been prepopulated in the previous query.
#不再查询数据库,因为第一次查询,数据已经填充进去了
print(article_obj.category.title)
2) mysql的悲观锁和乐观锁
3) drf内部内置了一些认证类,分别干了什么事
待填坑