1. 访问频率控制基本实现
可以根据 ip地址
来对用户访问频率进行限制,所以我们可以自定这样的访问频率控制的类:
还可以通过具有唯一标识意义的用户名或者用户ID等!
throttle.py:
import time
VISIT_RECORD = {}
class VisitThrottle:
def allow_request(self, request, view):
"""
设定10s之内只能访问10次
:param request:
:param view:
:return: True or False
返回值为True表示可以访问;返回值为False或None表示访问频率太高被限制
"""
# 获取用户的ip地址,当前request(封装)中有的就取当前的request,如果没有就到_request中取
remote_addr = request._request.META.get('REMOTE_ADDR')
print(remote_addr) # 127.0.0.1
ctime = time.time()
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [ctime]
return True
history = VISIT_RECORD.get(remote_addr)
while history and history[-1] < ctime - 10:
history.pop(-1)
if len(history) < 10:
history.insert(0, ctime)
return True
return False # 写不写都可以,如果执行到这里说明不可访问的,返回False或None都可以表示不可以访问
def wait(self, *args, **kwargs):
pass
views.py:
from rest_framework.views import APIView
from .models import User, UserToken
from django.http import JsonResponse
from utils.md5 import md5
from app.throttle import VisitThrottle
class AuthView(APIView):
authentication_classes = []
permission_classes = []
throttle_classes = [VisitThrottle, ]
def post(self, request, *args, **kwargs):
ret = {'code': 1000, 'msg': None}
try:
# 需要以form-data的方式提交
name = request._request.POST.get('name')
pwd = request._request.POST.get('pwd')
instance = User.objects.filter(name=name, pwd=pwd).first() # User object (1),
print(type(instance)) # <class 'app.models.User'>,加不加all()结果一样
print(instance) # User object (1),加不加all()结果一样
if not instance:
ret['code'] = 1001
ret['msg'] = '用户名或密码错误'
else:
token = md5(name=name)
UserToken.objects.update_or_create(user=instance, defaults={'token': token})
ret['token'] = token
except Exception as e:
ret['code'] = 1001
ret['msg'] = '请求异常'
return JsonResponse(ret)
10s内只能发送10次请求,超出10次请求,则会返回。请求被限制:
当请求被拒绝后,系统还可以返回 距离下次可以请求的时间
,可以通过wait方法来设置:
throttle.py:
import time
# 可以放到缓存中,默认Django REST framework中就是放在缓存中
VISIT_RECORD = {}
class VisitThrottle:
def __init__(self):
self.history = None
self.ctime = None
def allow_request(self, request, view):
"""
设定10s之内只能访问10次
:param request:
:param view:
:return: True or False
返回值为True表示可以访问;返回值为False或None表示访问频率太高被限制
"""
# 获取用户的ip地址,当前request(封装)中有的就取当前的request,如果没有就到_request中取
remote_addr = request._request.META.get('REMOTE_ADDR')
print(remote_addr) # 127.0.0.1
self.ctime = time.time() # 1593151531.1494734
print(self.ctime)
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [self.ctime]
return True
self.history = VISIT_RECORD.get(remote_addr)
while self.history and self.history[-1] < self.ctime - 10:
self.history.pop(-1)
if len(self.history) < 10:
self.history.insert(0, self.ctime)
return True
return False # 写不写都可以,如果执行到这里说明不可访问的,返回False或None都可以表示不可以访问
def wait(self, *args, **kwargs):
"""
设置距离下次可以请求的时间
:param args:
:param kwargs:
:return:默认返回None,表示使用默认
"""
"""
当请求被拒绝,会执行wait方法
"""
ctime = time.time()
print(ctime)
print(self.history[-1])
"""
return False
{
"detail": "Request was throttled. Expected available in 0 seconds."
}
"""
return 60 - (ctime - self.history[-1])
2. 访问频率控制源码流程
请求过来先走dispatch方法:
执行当前self.initialize_request方法:
执行self.check_throttles方法:
执行self.get_throttles方法,遍历访问控制类对象列表:
通过列表生成式生成访问控制类对象列表:
默认的访问控制类是从配置文件中获取的,也是全局的访问控制类:
如果访问被拒绝,即执行allow_request方法返回False或者None,则执行throttle.wait()方法:
3. 访问频率全局配置
同认证和权限一样在配置文件中进行配置:
settings.py:
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': ['app.throttle.VisitThrottle',]
}
同样某些请求可以不使用访问频率控制:
class AuthView(APIView):
authentication_classes = []
permission_classes = []
# 访问频率控制
throttle_classes = []
def post(self, request, *args, **kwargs):
pass
4. 内置访问频率控制类
Django REST framewor中内置了五个访问频率控制类,所有的访问权限控制类应该继承BaseThrottle并重写其中的allow_request(self, request, view)方法:
内置的访问控制类SimpleRateThrottle其实帮助我们实现了基于IP的访问权限控制,我们看它内部是怎么实现的:
所以之前自定义的类实现的功能完全可以继承SimpleRateThrottle来实现,首先自定义类继承SimpleRateThrottle,并设置scope:
from rest_framework.throttling import SimpleRateThrottle
class VisitThrottle(SimpleRateThrottle):
# scope被当作key使用的,根据它到配置文件中取值
scope = 'erics'
在配置文件中添加DEFAULT_THROTTLE_RATES:
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
# 每分钟访问3次
'erics': '3/m'
}
}
SimpleRateThrottle类中的逻辑是首先根据scope从配置文件中获取访问频率配置 3/m
:
然后执行将获取到的rate也就是scope传入parse_rate函数进行解析:
__init__构造函数执行完成之后,执行allow_request方法:
获取key,把ip当作key:
class VisitThrottle(SimpleRateThrottle):
# scope被当作key使用的,根据它到配置文件中取值
scope = 'erics'
def get_cache_key(self, request, view):
"""获取key"""
return self.get_ident()
接下来到缓存获取所有记录:
之前自定义的时间,wait方法也已经做好,我们不需要去写:
最终我们只需要几行代码就可以完成对匿名用户的访问频率的控制:
from rest_framework.throttling import SimpleRateThrottle
class VisitThrottle(SimpleRateThrottle):
# scope被当作key使用的,根据它到配置文件中取值
scope = 'erics'
def get_cache_key(self, request, view):
"""获取key"""
return self.get_ident(request)
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': ['app.throttle.VisitThrottle', ],
'DEFAULT_THROTTLE_RATES': {
# 每分钟访问5次,/右边只要首字母是m就可以了
'erics': '5/m'
}
}
匿名用户每分钟只能访问5次:
上面是对匿名用户的访问登录控制,也可以对登录用户做访问频率控制。只需要根据用户名来做频率控制条件:
from rest_framework.throttling import SimpleRateThrottle
class UserVisitThrottle(SimpleRateThrottle):
# scope被当作key使用的,根据它到配置文件中取值
scope = 'user_erics'
def get_cache_key(self, request, view):
"""获取key"""
print(request) # <rest_framework.request.Request object at 0x7f155c6b86d0>
# 用户认证成功之后就会有request.user
print(request.user) # User object (1)
print(request.user.name) # thanlon
return request.user.name
REST_FRAMEWORK = {
# 'DEFAULT_THROTTLE_CLASSES': ['app.throttle.VisitThrottle', ],
# 登录的用户使用根据用户名来做频率限制,匿名用户使用IP来做频率限制。这里全局配置了登录用户的,匿名用户需要可以单独设置通过ip来控制
'DEFAULT_THROTTLE_CLASSES': ['app.throttle.UserVisitThrottle', ],
'DEFAULT_THROTTLE_RATES': {
# 每分钟访问5次,/右边只要首字母是m就可以了
'erics': '5/m',
'user_erics': '10/m'
}
}
登录的用户每分钟只能访问10次: