接口访问控制(限流)
这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战
感激相遇 你好 我是y大壮
作者:y大壮
链接:juejin.cn/user/756923… 来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
🌊🌈关于前言:
文章部分内容及图片出自网络,如有问题请与我本人联系
🌊🌈关于内容:
1. 自定义方法实现(基本使用)
我们有的时候需要对api的访问进行限制,限制用户一天只可以访问多少次,就要用到限流,我们可以用IP,或者用户名,手机号等等的对其进行限制。
# 重写限流类
class hisitThrottle(BaseThrottle):
"""60秒只能访问3次"""
def __init__(self):
self.history = None
def allow_request(self, request, view):
"""返回True表示可以访问"""
remote_addr = request.META.get('REMOTE_ADDR') # 获取当前用户的IP
ctime = time.time()
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [ctime, ] # 注意这里是以列表形式添加
history = VISIT_RECORD.get(remote_addr) # 获取时间戳[]
self.history = history
print(self.history)
while history and history[-1] < ctime - 60: # 如果最后一个时间戳小于当前时间戳-60秒(就为True),
history.pop()
if len(history) < 3: # 只让访问3次
history.insert(0, ctime) # 把当前时间添加到第一个
return True
def wait(self):
"""还需要等待多少秒才可以执行"""
ctime = time.time()
return 60 - (ctime - self.history[0])
# 使用
class AuthLoginView(APIView):
"""用户登录"""
authentication_classes = []
permission_classes = []
throttle_classes = [hisitThrottle] # 使用
def post(self, request, *args, **kwargs):
ret = {'code': 200, 'message': 'ok!'}
try:
username = request._request.POST.get('username') # 获取前端传递过来的内容
password = request._request.POST.get('password')
obj = models.UserInfo.objects.filter(username=username, password=password).first()
if not obj:
ret['code'] = 1002
ret['message'] = "用户密码错误"
# 成功后为登录用户生产token
token = md5(username)
models.UserToken.objects.update_or_create(user=obj, defaults={'token': token})
ret['token'] = token
except Exception as e:
ret['code'] = 1003
ret['message'] = "请求异常"
return JsonResponse(ret)
2.限流流程原理
还是先dispatch 和原来的一样
def initial(self, request, *args, **kwargs):
# 实现认证
self.perform_authentication(request)
# 权限判断
self.check_permissions(request)
# 限流
self.check_throttles(request)
def check_throttles(self, request):
3、自定义全局认证&局部认证
全局限流
# 将写好的限流类放入一个文件夹
# app02.utils.throttle.py
import time
from rest_framework.throttling import BaseThrottle
VISIT_RECORD = {} # 访问记录 # ip{时间戳3,时间戳2,时间戳1} 当前时间在前面 (时间戳3,时间戳在一分钟以内)
class hisitThrottle(BaseThrottle):
"""60秒只能访问3次"""
def __init__(self):
self.history = None
def allow_request(self, request, view):
"""返回True表示可以访问"""
remote_addr = request.META.get('REMOTE_ADDR') # 获取当前用户的IP
ctime = time.time()
if remote_addr not in VISIT_RECORD:
VISIT_RECORD[remote_addr] = [ctime, ] # 注意这里是以列表形式添加
history = VISIT_RECORD.get(remote_addr) # 获取时间戳[]
self.history = history
print(self.history)
while history and history[-1] < ctime - 60: # 如果最后一个时间戳小于当前时间戳-60秒(就为True),
history.pop()
if len(history) < 3: # 只让访问3次
history.insert(0, ctime) # 把当前时间添加到第一个
return True
def wait(self):
"""还需要等待多少秒才可以执行"""
ctime = time.time()
return 60 - (ctime - self.history[0])
# 在settings.py中配置(全局配置)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ['app02.utils.auth.Authtication','app02.utils.auth.FirstAuthtication',],
# 'DEFAULT_AUTHENTICATION_CLASSES': ['app02.utils.auth.FirstAuthtication'], # 匿名用户(因为我们在封装的时候什么都没有写,让它返回None)
# 'UNAUTHENTICATED_USER': lambda: '匿名用户',
'UNAUTHENTICATED_USER': None,
'UNAUTHENTICATED_TOKEN': None,
'DEFAULT_PERMISSION_CLASSES':['app02.utils.permission.Mypermission'], # 管理才可以访问
'DEFAULT_THROTTLE_CLASSES':['app02.utils.throttle.hisitThrottle'] # 60秒访问3次限流
}
# !!! 如果有方法不想使用的话就 throttle_classes=[] 可以了
使用
视图函数就可以使用改权限
如果不想使用直接 throttle_classes = []
局部限流
class AuthLoginView(APIView):
"""用户登录"""
authentication_classes = []
permission_classes = []
throttle_classes = [hisitThrottle] # 局部配置
def post(self, request, *args, **kwargs):
ret = {'code': 1000, 'message': 'ok!'}
try:
username = request._request.POST.get('username') # 获取前端传递过来的内容
password = request._request.POST.get('password')
obj = models.UserInfo.objects.filter(username=username, password=password).first()
if not obj:
ret['code'] = 1002
ret['message'] = "用户密码错误"
# 成功后为登录用户生产token
token = md5(username)
models.UserToken.objects.update_or_create(user=obj, defaults={'token': token})
ret['token'] = token
except Exception as e:
ret['code'] = 1003
ret['message'] = "请求异常"
return JsonResponse(ret)
4、限流throttle(自带限流)
-
在文件夹中写限流类
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): """用户登录限制""" scope = "Luffy" def get_cache_key(self, request, view): return self.get_ident(request) # 获取用户的ip地址 class UserThrottle(SimpleRateThrottle): """api接口的限制""" scope = "LuffyUser" def get_cache_key(self, request, view): return request.user.username # 获取用户的名字
可选限流类
1) AnonRateThrottle
限制所有匿名未认证用户,使用IP区分用户。
使用
DEFAULT_THROTTLE_RATES['anon']
来设置频次2)UserRateThrottle
限制认证用户,使用User id 来区分。
使用
DEFAULT_THROTTLE_RATES['user']
来设置频次3)ScopedRateThrottle
限制用户对于每个视图的访问频次,使用ip或user id。
-
在settings.py中配置
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ['app02.utils.auth.Authtication','app02.utils.auth.FirstAuthtication',], # 'DEFAULT_AUTHENTICATION_CLASSES': ['app02.utils.auth.FirstAuthtication'], # 匿名用户(因为我们在封装的时候什么都没有写,让它返回None) # 'UNAUTHENTICATED_USER': lambda: '匿名用户', 'UNAUTHENTICATED_USER': None, 'UNAUTHENTICATED_TOKEN': None, 'DEFAULT_PERMISSION_CLASSES':['app02.utils.permission.Mypermission'], # 管理才可以访问 # 'DEFAULT_THROTTLE_CLASSES':['app02.utils.throttle.hisitThrottle'], # 60秒访问3次限流(自定义) "DEFAULT_THROTTLE_CLASSES": ["app02.utils.throttle.UserThrottle"], # 设置全局的 "DEFAULT_THROTTLE_RATES": { "Luffy": '3/m', # 用户登录限制 ,60秒3次 "LuffyUser": '10/m', # api接口的限制 } }
DEFAULT_THROTTLE_RATES
可以使用second
,minute
,hour
或day
来指明周期。 -
使用
# 全局 # 因为在settings.py中定义了一个全局的 "DEFAULT_THROTTLE_CLASSES": ["app02.utils.throttle.UserThrottle"], # 设置全局的 # 局部 class AuthLoginView(APIView): """用户登录""" authentication_classes = [] permission_classes = [] throttle_classes = [VisitThrottle,] # 通过ip def post(self, request, *args, **kwargs): ret = {'code': 1000, 'message': 'ok!'} try: username = request._request.POST.get('username') # 获取前端传递过来的内容 password = request._request.POST.get('password') obj = models.UserInfo.objects.filter(username=username, password=password).first() if not obj: ret['code'] = 1002 ret['message'] = "用户密码错误" # 成功后为登录用户生产token token = md5(username) models.UserToken.objects.update_or_create(user=obj, defaults={'token': token}) ret['token'] = token except Exception as e: ret['code'] = 1003 ret['message'] = "请求异常" return JsonResponse(ret)