在Flask中使用API密钥进行认证有 accepted的方法吗?

51 投票
3 回答
44800 浏览
提问于 2025-04-17 19:14

我有一个小的API,想给它加上身份验证。我希望能够为使用这个API的人生成API密钥;这样他们在发送请求的时候就可以把这些密钥包含在里面。

有没有Flask的库可以做到这一点?或者有没有什么常见的方法可以实现?我搜索了一下,只找到这个,但内容并没有深入讲解。我希望能找到一个相关的库,如果有的话。

3 个回答

2

生成API密钥的“典型”方法是创建一个UUID(通用唯一标识符)。通常是通过对一些用户信息和一些随机信息(比如当前时间)进行md5哈希处理来实现的。

不过,所有的API密钥都应该是UUID格式。md5生成的十六进制哈希符合这个要求,但其实还有其他方法可以做到。

一旦你为用户创建了密钥,就把它存储在数据库中,作为用户信息的一部分。同时要检查用户的密钥(通常存储在cookie里)是否和你存的匹配。具体的操作方式在你链接的页面中有一些描述。

10

这里有一个使用 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('==')

在应用中实现这个功能的一个可能方法是,在你想要保护的每个路由上应用一个装饰器。

举个例子:

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

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

@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
   """

这个例子使用 SQLAlchemy 来把密钥存储在数据库中(你也可以使用 SQLite)。

你可以在这里查看具体实现:https://github.com/haukurk/flask-restapi-recipe

14

对于认证密钥,首先要生成一个随机值,并把这个值存储到数据库里。这里用的 random() 方法生成的随机性不够,所以应该使用 os.urandom()

你发的链接里有一个很好的例子,讲的是怎么用装饰器函数来处理这些事情。在这个装饰器函数里,首先检查请求中是否有设置 appkey 的值,然后在数据库里验证这个值是否有效,最后再返回原来的函数。如果 appkey 无效,就用 raise AuthenticationError("Invalid appkey") 抛出一个错误,这样就完成了。

你链接的那个例子有点让人困惑。我觉得 如何制作一系列函数装饰器? 的演示更好。

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

撰写回答