1.权限源码分析
1.APIView源码497行:self.initial(request, *args, **kwargs)中进行了三大认证。
2.在initial的源码中,以下三行代码是进行三大认证的代码:
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
# 按照顺序从上往下执行,先执行认证,再执行权限认证,最后执行频率认证。三大认证走不完视图类也不会走。
self.check_permissions(request)进行了权限认证,依然是在APIView中找到了check_permissions方法:
def check_permissions(self, request):
for permission in self.get_permissions():
...
发现有一个get_permissions()方法。
3.依然是在APIView中,找到了get_permissions()方法:
def get_permissions(self):
return [permission() for permission in self.permission_classes]
self是视图类的对象,视图类BookView中我们添加权限认证时已经正好添加了permission_classes。并且permission_classes可以有多个值,返回值permission()就是一个个权限类的对象。
4.回到check_permissions:
def check_permissions(self, request):
# 此时的self.get_permissions()就是一个个权限类对象,用permission进行for循环
for permission in self.get_permissions():
# 用permission点方法has_permission(),这也是我们重写has_permission()方法需要返回布尔值的原因。
if not permission.has_permission(request, self):
# 如果has_permission返回了None,权限类对象点方法permission_denied()时权限认证就会停止。所以只要有一个权限类认证不通过,那么就无法通过。
self.permission_denied(
request,
# permission_denied方法有属性messgae和code,message就是在前端提示的detail错误信息,code是提示码
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
"""
总结:
-APIView---dispatch----》initial---》倒数第二行---》self.check_permissions(request)
里面取出配置在视图类上的权限类,实例化得到对象,一个个执行对象的has_permission方法,如果返回False,就直接结束,不再继续往下执行,权限就认证通过
-如果视图类上不配做权限类:permission_classes = [CommonPermission],会使用配置文件的api_settings.DEFAULT_PERMISSION_CLASSES
优先使用项目配置文件,其次使用drf内置配置文件
"""
2.认证源码分析
1.和之前一样,APIView中497行会执行三大认证,进入到initial源码中:
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
进入到perform_authentication()方法。
2.perform_authentication()代码如下:
def perform_authentication(self, request):
request.user
此时用ruquest点数据user,此时需要进入到类Request中查找user的来源。
"""
1.user代码如下,可以看出user是被伪装成数据的方法:
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
# self是Request的对象,现在要从Request中找_authenticate()方法
return self._user
2._authenticate()方法代码如下:
def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""
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()
3.上述for循环self.authenticators,点进self.authenticators,
class Request:
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
self.authenticators = authenticators or ()
说明authenticators属性是在类Request产生对象时生成,
"""
3.在APIView中391行中,包含了新的request:
def initialize_request(self, request, *args, **kwargs):
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
# authenticators这时候传进去了
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
4.self.get_authenticators()是生成authenticators这时候传进去了属性,点进去方法get_authenticators():
def get_authenticators(self):
return [auth() for auth in self.authentication_classes]
发现get_authenticators()方法的返回值是视图类中一个个认证类。
5.再回到类Request中的方法_authenticate():
def _authenticate(self):
for authenticator in self.authenticators:
try:
# self是Request对象,所以此时调用了我们自己定义的认证类中的方法:authenticate(self),并且这里的self是自定义类方法中的参数request:
# 自定义类方法:def authenticate(self, request):
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise
# 如果拿到的元组不为空,说明在自定义认证类中认证成功,token表中有该字符串,此时直接结束for循环。所以认证类只要有一个类认证成功直接结束。而权限类需要所有的类全部校验完成才算成功。
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
# 解压赋值:self.user是用户对象(token字符串对应的用户), self.auth是token字符串。此时的self是Request对象,所以拿到的是用户对象。
return
# 返回None则继续下一个视图类,所以多个认证类时只需要最后一个认证类返回两个结果就可以,其他的返回None。
self._not_authenticated()
"""
总结:
1 配置在视图类上的认证类,会在执行视图类方法之前执行,在权限认证之前执行
2 自己写的认证类,可以返回两个值或None
3 后续可以从request.user 取出当前登录用户(前提是你要在认证类中返回)
"""
3.自定义频率类
1.思路:
1.取出访问者ip
2.判断当前ip在不在访问字典内,如果不在则添加进去,并且直接返回True,表示第一次访问,在字典里。继续往下走,数据类型按照如下格式:{ip地址:[时间1,时间2,时间3,时间4]}
3.循环判断当前ip的列表,并且保证列表不为空,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问
4.判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
5.当大于等于3,说明一分钟内访问超过三次,返回False验证失败
2.代码:
from rest_framework.throttling import BaseThrottle
import time
#
class SuperThrottle(BaseThrottle):
VISIT_RECODE = {}
def __init__(self):
self.history = None
def allow_request(self, request, view):
ip = request.META.get('REMOTE_ADDR')
ctime = time.time()
if not ip in self.VISIT_RECODE:
self.VISIT_RECODE[ip] = [ctime,]
self.history = self.VISIT_RECODE.get(ip,[])
while self.history and ctime - self.history[-1] > 60:
self.history.pop()
if len(self.history) < 4:
self.history.insert(0,ctime)
return True
else:
return False
4.频率源码分析
1.APIView中:
self.initial(request, *args, **kwargs)执行三大认证,
def initial():
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
其中self.check_throttles(request)执行频率认证。
2.check_throttles()代码:self频率类的对象。
def check_throttles(self, request):
throttle_durations = []
for throttle in self.get_throttles():
if not throttle.allow_request(request, self):
throttle_durations.append(throttle.wait())
if throttle_durations:
durations = [
duration for duration in throttle_durations
if duration is not None
]
duration = max(durations, default=None)
self.throttled(request, duration)
3.get_throttles()代码:返回的对象是一个频率类列表。
def get_throttles(self):
return [throttle() for throttle in self.throttle_classes]
5.基于APIView编写分页
views.py:
from .commonLimitOffsetPagination import CommonLimitOffsetPagination
class BookView(ViewSetMixin,APIView):
def list(self,request):
books = Book.objects.all()
paginator = CommonLimitOffsetPagination()
page = paginator.paginate_queryset(books,request,self)
if page is not None:
serializer = BookSerializer(instance=page,many=True)
return Response({
'total':paginator.count,
'next':paginator.get_next_link(),
'previous':paginator.get_previous_link(),
'results':serializer.data
})
commonLimitOffsetPagination.py:
from rest_framework.pagination import LimitOffsetPagination
class CommonLimitOffsetPagination(LimitOffsetPagination):
default_limit = 3
limit_query_param = 'limit'
offset_query_param = 'offset'
max_limit = 5
6.异常处理
1.在drf中通过如下方式导入drf异常:
from rest_framework.exceptions import APIException
如果我们主动在代码中抛出drf异常,那么前端会处理成json格式,非drf错误不管是主动抛出还是非主动抛出的错误,都无法自动捕获并处理:
2.我们的目标是,不管什么错误(主动还是非主动)都处理成:{"code":999,"msg":"系统错误,请联系管理员"}
3.代码:
# 新建一个py文件
exception_handler.py:
from rest_framework.views import exception_handler
from rest_framework.response import Response
def common_exception_handler(exc, context):
# 调用函数exception_handler()
res = exception_handler(exc, context)
# 如果有值说明是drf错误
if res:
res = Response({'code':101,'msg':res.data.get('detail')})
else:
# 没有值说明是非drf错误,针对这种错误统一处理,msg也可以直接写死:'系统错误,请联系系统管理员'
res = Response({'code':102,'msg':str(exc)})
return res
setting.py:
REST_FRAMEWORK = {
'EXCEPTION_HANDLER':'app01.exception_handler.common_exception_handler',
}
标签:get,APIView,self,permission,request,认证,源码,def,三大
From: https://www.cnblogs.com/zkz0206/p/17103362.html