【问题标题】:Django REST Framework: Per-user throttlesDjango REST Framework:每用户限制
【发布时间】:2016-04-04 23:15:52
【问题描述】:

我有一些用户需要非常高的油门,以便他们可以大量使用系统。有没有一种简单的方法可以让他们比其他用户更高的油门?

我环顾四周,但没有找到任何东西。

【问题讨论】:

    标签: django django-rest-framework throttling


    【解决方案1】:

    我想出了一种方法,方法是扩展 UserRateThrottle 并将特殊用户添加到我的设置文件中。

    这个类只是重写了allow_request方法,添加了一些特殊的逻辑来查看OVERRIDE_THROTTLE_RATES变量中是否列出了用户名:

    class ExceptionalUserRateThrottle(UserRateThrottle):
        def allow_request(self, request, view):
            """
            Give special access to a few special accounts.
    
            Mirrors code in super class with minor tweaks.
            """
            if self.rate is None:
                return True
    
            self.key = self.get_cache_key(request, view)
            if self.key is None:
                return True
    
            self.history = self.cache.get(self.key, [])
            self.now = self.timer()
    
            # Adjust if user has special privileges.
            override_rate = settings.REST_FRAMEWORK['OVERRIDE_THROTTLE_RATES'].get(
                request.user.username,
                None,
            )
            if override_rate is not None:
                self.num_requests, self.duration = self.parse_rate(override_rate)
    
            # Drop any requests from the history which have now passed the
            # throttle duration
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
            return self.throttle_success()
    

    要使用它,只需将您的DEFAULT_THROTTLE_CLASS 设置为此类,然后将一些特殊用户放入OVERRIDE_THROTTLE_RATES,如下所示:

    'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle',
        'cl.api.utils.ExceptionalUserRateThrottle',
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',
        'user': '1000/hour',
    },
    'OVERRIDE_THROTTLE_RATES': {
        'scout': '10000/hour',
        'scout_test': '10000/hour',
    },
    

    【讨论】:

    • 感谢您的回答!这个想法非常简洁,绝对可以包含在 DRF 中
    • 我没有尝试将其推向上游,但如果有人尝试将其放入主代码库,我会很高兴。
    • 对我来说,理想的解决方案应该是在 django 管理页面中按组对用户进行分组,并按这些组进行限制。试图在你的代码和这个想法之间做一个混合。如果我想出一些东西会在这里发布
    【解决方案2】:

    我在自定义Django REST Throttling后找到了解决方案,

    在 3 次登录尝试后阻止特定用户(阻止我的应用程序中出现的 user_id)。匿名用户 6 次登录尝试后阻止 IP 地址。

    prevent.py :-

    #!/usr/bin/python
    
    from collections import Counter
    
    from rest_framework.throttling import SimpleRateThrottle
    from django.contrib.auth.models import User
    
    
    class UserLoginRateThrottle(SimpleRateThrottle):
        scope = 'loginAttempts'
    
        def get_cache_key(self, request, view):
            user = User.objects.filter(username=request.data.get('username'))
            ident = user[0].pk if user else self.get_ident(request)
    
            return self.cache_format % {
                'scope': self.scope,
                'ident': ident
            }
    
        def allow_request(self, request, view):
            """
            Implement the check to see if the request should be throttled.
            On success calls `throttle_success`.
            On failure calls `throttle_failure`.
            """
            if self.rate is None:
                return True
    
            self.key = self.get_cache_key(request, view)
            if self.key is None:
                return True
    
            self.history = self.cache.get(self.key, [])
            self.now = self.timer()
    
            while self.history and self.history[-1] <= self.now - self.duration:
                self.history.pop()
    
            if len(self.history) >= self.num_requests:
                return self.throttle_failure()
    
            if len(self.history) >= 3:
                data = Counter(self.history)
                for key, value in data.items():
                    if value == 2:
                        return self.throttle_failure()
            return self.throttle_success(request)
    
        def throttle_success(self, request):
            """
            Inserts the current request's timestamp along with the key
            into the cache.
            """
            user = User.objects.filter(username=request.data.get('username'))
            if user:
                self.history.insert(0, user[0].id)
            self.history.insert(0, self.now)
            self.cache.set(self.key, self.history, self.duration)
            return True
    

    Views.py :-

    from .prevent import UserLoginRateThrottle
       ....
       ....
       ....
       class ObtainAuthToken(auth_views.ObtainAuthToken):
           throttle_classes = (UserLoginRateThrottle,)/use this method here your login view
    
           def post(self, request, *args, **kwargs):
               ....
               ....
    

    Settings.py :-

    Django-rest-framework

    REST_FRAMEWORK = {
        ...
        ...
        ...
        'DEFAULT_THROTTLE_CLASSES': (
            'rest_framework.throttling.UserRateThrottle',
    
        ),
        'DEFAULT_THROTTLE_RATES': {
            'loginAttempts': '6/hr',
            'user': '1000/min',
        }
    }
    

    【讨论】:

    • 你为什么要在throttle_success中这样做!? if user: self.history.insert(0, user[0].id) user.id 和 time 是两个不同的属性,对吧?
    • 这是如何回答问题的?用户询问的是如何增加特定用户的访问权限,而不是如何阻止登录验证失败的用户
    【解决方案3】:

    我知道这是一个非常古老的线程,并且接受的答案对我也有帮助。我想展示如何设置多个用户速率限制,在这种情况下为 root 用户设置额外的限制

    from rest_framework.settings import api_settings
    from django.core.exceptions import ImproperlyConfigured
    
    class RootRateThrottle(UserRateThrottle):
        """
        Limits the rate of API calls that may be made by a given user.
        The user id will be used as a unique cache key if the user is
        authenticated.  For anonymous requests, the IP address of the request will
        be used.
        """
    
        def get_cache_key(self, request, view):
            if request.user.is_authenticated:
                ident = request.user.pk
            else:
                ident = self.get_ident(request)
    
            self.rate = self.get_rate(request)
            logger.debug(
                "Throttling rate for %s: %s", request.user, self.rate
            )
    
            self.num_requests, self.duration = self.parse_rate(self.rate)
            return self.cache_format % {
                'scope': self.scope,
                'ident': ident
            }
    
        def get_rate(self, request=None):
            """
            Determine the string representation of the allowed request rate.
            """
            if not getattr(self, 'scope', None):
                msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
                       self.__class__.__name__)
                raise ImproperlyConfigured(msg)
    
            if request and request.user.is_superuser:
                throttle_rates = settings.REST_FRAMEWORK["ROOT_THROTTLE_RATES"]
            else:
                throttle_rates = api_settings.DEFAULT_THROTTLE_RATES
            try:
                return throttle_rates[self.scope]
            except KeyError:
                msg = "No default throttle rate set for '%s' scope" % self.scope
                raise ImproperlyConfigured(msg)
    
    class ByMinuteRateThrottle(RootRateThrottle):
        scope = 'minute'
    
    
    class ByHourRateThrottle(RootRateThrottle):
        scope = 'hour'
    
    
    class ByDayRateThrottle(RootRateThrottle):
        scope = 'day'
    

    设置部分如下所示

    'DEFAULT_THROTTLE_CLASSES': [
        'threedi_api.throttling.ByMinuteRateThrottle',
        'threedi_api.throttling.ByHourRateThrottle',
        'threedi_api.throttling.ByDayRateThrottle',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'minute': '100/min',
        'hour': '1000/hour',
        'day': '5000/day',
    },
    'ROOT_THROTTLE_RATES': {
        'minute': '200/min',
        'hour': '2000/hour',
        'day': '10000/day',
    },
    

    【讨论】:

    • 这看起来很有用,但是你把代码放在哪里了?
    猜你喜欢
    • 2022-11-05
    • 2015-05-18
    • 2013-07-11
    • 1970-01-01
    • 2012-03-21
    • 2018-10-19
    • 1970-01-01
    • 1970-01-01
    • 2018-05-20
    相关资源
    最近更新 更多