Flask Limiter - 为单个端点

时间:2017-11-13 23:25:13

标签: python flask api-design

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

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(...):
    .......

在单个软件包(即flask_limiter)中实现所有速率限制会很不错,但到目前为止,我认为在多个密钥上实现速率限制的唯一方法是切换到像redis这样的东西。

有关使用flask_limiter实现多键速率限制的方法的任何线索?

1 个答案:

答案 0 :(得分:0)

如果有人有兴趣,我决定切换到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}

然后装饰烧瓶终点:

@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():
    ....