Python 在 Flask 中是否有一种可接受的方式使用 API 密钥进行身份验证?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15439055/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 20:07:21  来源:igfitidea点击:

Is there an accepted way to use API keys for authentication in Flask?

pythonapiflaskapi-key

提问by NickAldwin

I have a small API that I'd like to add authentication to. I'd like to be able to generate API keys for API consumers; the consumers can then use include the keys with their requests requests.

我有一个小的 API,我想向其中添加身份验证。我希望能够为 API 使用者生成 API 密钥;然后消费者可以在他们的请求请求中使用包含密钥。

Is there a Flask library which does something like this? Or is there a typical way to do it? I did a search and I only really came upon this, which doesn't really go very much in depth. I'm looking for a library if there is one.

是否有一个 Flask 库可以做这样的事情?或者有没有典型的方法来做到这一点?我做了一个搜索,我只是真正发现了这个,它并没有真正深入。如果有图书馆,我正在寻找图书馆。

回答by jeffknupp

The "typical" way to generate an API key is to create a UUID (usually by creating an md5 hash of some subset of user information + somewhat random information (like current time)).

生成 API 密钥的“典型”方法是创建一个 UUID(通常通过创建一些用户信息子集的 md5 哈希值 + 一些随机信息(如当前时间))。

All API keys, however, should be UUIDs. The hexadecimal hash created by md5 meets this requirement, but there are certainly other methods.

但是,所有 API 密钥都应该是 UUID。md5 创建的十六进制哈希满足这个要求,但肯定还有其他方法。

Once you've created a key for the user, store it in the database as part of the user information and check that their key (stored in a cookie, usually) matches what you have. The actual mechanics of this are (somewhat) described in the page you linked to.

为用户创建密钥后,将其作为用户信息的一部分存储在数据库中,并检查他们的密钥(通常存储在 cookie 中)是否与您拥有的匹配。在您链接到的页面中(在某种程度上)描述了其实际机制。

回答by Jeff Ferland

For authentication keys, create a random value and store that value in a database. random()provides insufficient entropy for things like this, so use os.urandom().

对于身份验证密钥,创建一个随机值并将该值存储在数据库中。random()为此类事情提供的熵不足,因此请使用os.urandom().

The link you posted to has a very good example of how to handle things with a decorator function. In the decorator function, check the appkey value is set in the request, verify it is valid in the database, and then return the function. If the appkey is invalid, raise AuthenticationError("Invalid appkey")and you're done.

您发布的链接有一个很好的示例,说明如何使用装饰器函数处理事物。在装饰器函数中,检查请求中设置的appkey值,在数据库中验证是否有效,然后返回函数。如果 appkey 无效,raise AuthenticationError("Invalid appkey")那么你就完成了。

The example you linked to is a bit confusing. I like the demonstration from How to make a chain of function decorators?better.

您链接到的示例有点令人困惑。我喜欢如何制作函数装饰器链中的演示更好的。

def checkAppKey(fn):
    def inner(*args, **kwargs): #appkey should be in kwargs
        try:
            AppKey.get(appkey)
        except KeyError:
            raise AuthenticationError("Invalid appkey")
            #Whatever other errors can raise up such as db inaccessible
        #We were able to access that API key, so pass onward.
        #If you know nothing else will use the appkey after this, you can unset it.
        return fn(*args, **kwargs)
    return inner

回答by Haukur Kristinsson

Here is an function that uses hashlib which has worked fairly well for me:

这是一个使用 hashlib 的函数,它对我来说效果很好:

def generate_hash_key():
    """
    @return: A hashkey for use to authenticate agains the API.
    """
    return base64.b64encode(hashlib.sha256(str(random.getrandbits(256))).digest(),
                            random.choice(['rA', 'aZ', 'gQ', 'hH', 'hG', 'aR', 'DD'])).rstrip('==')

A possible solution to implement this in the app could be applying a decorator on each route that you want to protect.

在应用程序中实现这一点的可能解决方案可能是在您想要保护的每条路线上应用装饰器。

Example:

例子:

def get_apiauth_object_by_key(key):
    """
    Query the datastorage for an API key.
    @param ip: ip address
    @return: apiauth sqlachemy object.
    """
    return model.APIAuth.query.filter_by(key=key).first()

def match_api_keys(key, ip):
    """
   Match API keys and discard ip
   @param key: API key from request
   @param ip: remote host IP to match the key.
   @return: boolean
   """
   if key is None or ip is None:
      return False
   api_key = get_apiauth_object_by_key(key)
   if api_key is None:
      return False
   elif api_key.ip == "0.0.0.0":   # 0.0.0.0 means all IPs.
      return True
   elif api_key.key == key and api_key.ip == ip:
      return True
   return False

def require_app_key(f):
   """
   @param f: flask function
   @return: decorator, return the wrapped function or abort json object.
   """

   @wraps(f)
   def decorated(*args, **kwargs):
      if match_api_keys(request.args.get('key'), request.remote_addr):
         return f(*args, **kwargs)
      else:
         with log_to_file:
            log.warning("Unauthorized address trying to use API: " + request.remote_addr)
         abort(401)
      return decorated

And then you can use the decorator as such:

然后你可以像这样使用装饰器:

@require_app_key
def delete_cake(version, cake_id):
   """
   Controller for API Function that gets a cake by ID
   @param cake_id: cake id
   @return: Response and HTTP code
   """

This example uses SQLAlchemy to store keys in database (You could use SQLite).

此示例使用 SQLAlchemy 将密钥存储在数据库中(您可以使用 SQLite)。

You can see the implementation here: https://github.com/haukurk/flask-restapi-recipe.

您可以在此处查看实现:https: //github.com/haukurk/flask-restapi-recipe