RESF framework框架-接口访问控制(限流)源码流程解析

210 阅读4分钟

接口访问控制(限流)

这是我参与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(自带限流)

  1. 在文件夹中写限流类

    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。

  2. 在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, hourday来指明周期。

  3. 使用

    # 全局
    # 因为在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)