Flask Limiter - 对单个端点使用多个 key_func

Col*_*Wal 4 python api-design flask

我正在努力使用flask和flask_limiter构建一个API端点,但我遇到了一个问题。我的目标是在单个端点上对客户端 IP 地址以及客户端提供的 API 密钥(可通过 Flask 的“请求”模块访问)进行速率限制。

IP 地址限制器很重要,因为我想防止暴力攻击,而 API 密钥限制器只是出于商业原因。我已经构建了两个速率限制器,并且它们独立工作(即我可以限制 IP 地址或提供的 API 密钥),但我无法同时在端点上运行这两个速率限制器。到目前为止我尝试过的一个例子:

from flask import Flask, request
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

app = Flask(__name__)
ip_limiter = Limiter(app, key_func=get_remote_address)
get_api_key = lambda : request.args.get('apikey')
api_key_limiter = Limiter(app, key_func=get_api_key)

@app.route('/theEndpoint')
@ip_limiter.limit('some_limit_here')
@api_key_limiter.limit('other_limit_here')
def theEndpointFunction(...):
    .......
Run Code Online (Sandbox Code Playgroud)

最好在单个包(即flask_limiter)中实现所有速率限制,但到目前为止,我认为对多个键实现速率限制的唯一方法是切换到redis之类的东西。

关于使用 Flask_limiter 实现多键速率限制的方法有什么线索吗?

Col*_*Wal 6

如果有人感兴趣,我决定切换到 Redis 来实现我的双密钥速率限制。解决方案是这样的:

class RateLimit(object):
    def __init__(self, key, max_requests, seconds):
        self.reset = int(time.time()) + seconds
        self.key = key
        self.max_requests = max_requests
        self.seconds = seconds
        p = redis.pipeline()
        p.incr(self.key)
        p.expireat(self.key, self.reset)
        self.current = min(p.execute()[0], max_requests)
    remaining = property(lambda x: x.max_requests - x.current)
    over_limit = property(lambda x: x.current >= x.max_requests)


def get_view_rate_limit():
    return getattr(g, '_view_rate_limit', None)

def over_limit(limit):
    #formatting a JSON response
    response = {"result": "Max number of requests exceeded", 
                "status": False}
    response = jsonify(response)
    response.status_code = 400
    return response

def ratelimit(max_requests, seconds, key_func, over_limit=over_limit):
    def decorator(f):
        def rate_limited(*args, **kwargs):
            key = key_func()
            rlimit = RateLimit(key, max_requests, seconds)
            g._view_rate_limit = rlimit
            if over_limit is not None and rlimit.over_limit:
                return over_limit(rlimit)
            return f(*args, **kwargs)
        return update_wrapper(rate_limited, f)
    return decorator


RATE_LIMIT_FUNCS = {'apikey': lambda: request.args.get('apikey'),
                    'ip': lambda: get_remote_address}
Run Code Online (Sandbox Code Playgroud)

然后装饰烧瓶端点:

@app.route('/theEndpoint')
@ratelimit(max_requests=5, seconds=300, key_func=RATE_LIMIT_FUNCS['apikey'])
@ratelimit(max_requests=15, seconds=300, key_func=RATE_LIMIT_FUNCS['ip'])
def theEndpointFunction():
    ....
Run Code Online (Sandbox Code Playgroud)