目录
权限类
一、认证
-
校验用户是否登录,登录认证
-
用户登录后,某个接口限定只有超级用户才能访问,普通用户不能访问
二、需求
- 出版社所有的接口,必须先登录,而且是超级管理员才能访问
三、使用步骤
- 第一步:写一个类,继承
BasePermission
- 第二步:重写
has_permission
方法 - 第三步:在方法中校验用户是否有权限(
request.user
就是当前登录用户) - 第四步:有权限,返回True;没有权限,返回False
- 第五步:
self.message
是给前端的提示信息 - 第六步:局部使用;全局使用,局部禁用
from rest_framework.permissions import BasePermission
class UserTypePermission(BasePermission):
def has_permission(self, request, view):
# 只有超级管理员有权限
if request.user.user_type == 1:
return True # 有权限
else:
# self.message = '普通用户和游客用户都没有权限' # 返回给前端的提示
# 使用choice后,user.user_type拿到的数字类型,想变成对应的字符串user.get_user_type_display()
self.message = '您是:%s,您没有权限' % request.user.get_user_type_display()
return False # 没有权限
频率类
一、应用
短时间内大量的访问会导致程序崩溃
无论是否登录,是否有权限都要限制访问的频率
eg:一分钟访问限最多三次
二、使用步骤
-
第一步:写一个类,继承
SimpleRateThrottle
-
第二步:重写
get_cache_key
,返回唯一的字符串,会以这个字符串做频率限制 -
第三步:写一个类属性
scope='随意写'
,必须要跟配置文件对应 -
第四步:配置文件中写
'DEFAULT_THROTTLE_RATES': {'frequency': '5/m'},
-
第五步:局部使用;全局使用,局部禁用
from rest_framework.throttling import SimpleRateThrottle, BaseThrottle
class Throttle(SimpleRateThrottle):
# 类属性,这个属性可以随意命名,但是要和配置文件对应
scope = 'frequency'
def get_cache_key(self, request, view):
# 返回什么,就以什么限制频率
# 可以通过用户id 或 IP地址
return request.META.get('REMOTE_ADDR')
# 配置文件中
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {'frequency': '5/m'},
}
源码分析
'''
写个认证类,重写某个方法,配置在实体类上,就有了认证
--->
只要加了认证类,在视图类的方法中的request.user就是当前登录用户
--->
猜测:认证类的执行,是在视图类的方法之前执行的
'''
# 源码分析
- 之前读APIView的执行流程:
包装了新的request,
执行了三大认证,
执行视图类的方法,
处理了全局异常
- 入口:APIView的dispatch
- APIView的dispatch里的异常捕获里
self.initial(request, *args, **kwargs)
- APIView的initial里有三句话,分别是:认证,权限,频率
self.perform_authentication(request) # 认证
self.check_permissions(request) # 权限
self.check_throttles(request) # 频率
一、认证源码分析
- 读认证类的源码
def perform_authentication(self, request):
request.user # 新的request
- request是新的request --> Request类中找'user'属性(方法包装成了属性)
- 到Request类中找:220行
def user(self):
if not hasattr(self, '_user'): # Request类的对象中反射_user
with wrap_attributeerrors():
self._authenticate() # 第一次一定会走这个代码
return self._user
- Request的self._authenticate():373行
def _authenticate(self):
for authenticator in self.authenticators: # 配置在视图类中所有的认证类的对象
try:
# user_auth_tuple:(user_token,token)
user_auth_tuple = authenticator.authenticate(self) # 调用认证类对象的authenticator
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()
'''
总结:
认证类:要重写authenticator方法
认证通过:返回两个值或None
认证不通过:抛认证不通过抛AuthenticationFailed(继承了APIException)异常
'''
二、权限源码分析
- 最简单的权限执行流程:APIView的check_permissions(),325行
def check_permissions(self, request):
for permission in self.get_permissions():
# permission是配置在视图类中权限类的对象,对象调用他的绑定方法has_permission
# 对象调用自己的绑定方法会把自己传入(self, request, view)=(权限类的对象,request,视图类的对象)
if not permission.has_permission(request, self): # 这个self是APIView的对象,也就是传入的view
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
- APIView的self.get_permissions(),274行
def get_permissions(self):
return [permission() for permission in self.permission_classes]
- self.permission_classes 就是在视图类中配的'权限类'的列表
- 所以这个get_permissions返回的是:在视图类中配的'权限类的对象'列表[UserTypePermission(),]
'''
总结:
- 为什么要写一个类,重写has_permission方法,有三个参数
对象调用自己的绑定方法会把自己传入,permission调用has_permission,
第三个参数view就是传给if not permission.has_permission(request, self) 的 self
- 为什么一定要return True或False
要用来做if判断
- messgage可以做什么用
给前端的提示
'''
三、频率类源码
- APIView的check_throttles:352行
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)
'''
总结:
要写频率类,必须重写allow_request方法
没有到频率的限制:返回True
到频率限制:返回False
'''
鸭子类型
一、通俗化的解释
走路像鸭子,说话像鸭子,它就是鸭子
二、详细说明
在Python中:
指的是面向对象中,子类不需要显示的继承某个类,只要有某个类中的方法和属性,那就属于这个类
在其他的语言中:
必须要继承某个类,才属于这个类
假设有个鸭子类Duck类,有两个方法:run,speak
1.其他语言中:
# 假设又有一个普通鸭子类:PDuck
如果它也是鸭子,它需要继承Duck类,只要继承了鸭子类,什么都不需要写,普通鸭子类的对象就是鸭子这种类型;
如果不继承,普通鸭子类的对象就不是鸭子这种类型
# 假设又有一个唐老鸭子类:TDuck
如果它也是鸭子,它需要继承Duck类,只要继承了鸭子类,什么都不需要写,唐老鸭子类的对象就是鸭子这种类型;
如果不继承,唐老鸭子类的对象就不是鸭子这种类型
2.在python中:
只要PDuck中有run和speak方法,PDuck就是属于Duck这个类
# 缺陷:
如果使用python鸭子类型的写法,如果方法名写错了,就不属于鸭子类,容易出问题
# 解决缺陷:
- 方式一:
abc模块装饰后,必须重写方法,不重写就报错
- 方法二:
drf源码中使用的:父类中写这个方法,但没有具体实现,直接抛异常
注意
django中的配置文件不要最易导入,可能会出错
django的运行是在加载完配置文件之后才运行
在配置文件中导入模块,会先执行模块,而这个模块中导入的其他模块必需要django运行才能使用
作业
class BThrottle(BaseThrottle):
ask_dict = {}
def allow_request(self, request, view):
# 自定义逻辑{ip:[时间1,时间2]}
# 1.取出访问者IP
# 2.判断当前IP是否在访问字典内,没有:添加,返回True,表示第一次访问;有:继续往下走
# 3.循环判断当前IP的列表,有值,并且当前时间减去列表最后一个时间大于60s,pop这种数据,这样列表中只有60s以内的访问时间
# 4.判断,当列表小于5,说明一分钟内访问不足5次,把当前时间插入到列表第一个位置,返回True,顺利通过
# 5.当列表大于等于5,说明一分钟内访问超过五次,返回False验证失败
# user_ip = request.META.get('REMOTE_ADDR')
user_id = request.user.id
time1 = time.time()
if user_id not in self.ask_dict:
self.ask_dict[user_id] = [time1, ]
return True
else:
last_time = self.ask_dict.get(user_id)
if time1 - last_time[0] > 60:
self.ask_dict[user_id] = [time1, ]
return True
else:
count = len(last_time)
if count >= 5:
return False
else:
last_time.insert(0, time1)
return True
标签:permission,self,request,源码,user,权限,鸭子
From: https://www.cnblogs.com/Zhang614/p/16773775.html