我有一个小 API,我想向其中添加身份验证。我希望能够为 API 消费者生成 API 密钥;然后,消费者可以使用将密钥包含在他们的请求中。
有没有做这样的事情的 Flask 库?或者有没有典型的方法来做到这一点?我做了一个搜索,我才真正发现了这个,它并没有真正深入。如果有图书馆,我正在寻找图书馆。
对于身份验证密钥,创建一个随机值并将该值存储在数据库中。random()
为这样的事情提供不足的熵,所以使用os.urandom()
.
您发布的链接有一个很好的例子,说明如何使用装饰器功能处理事情。在装饰器函数中,检查请求中设置的appkey值,验证它在数据库中是否有效,然后返回函数。如果 appkey 无效,raise AuthenticationError("Invalid appkey")
那么您就完成了。
您链接到的示例有点令人困惑。我喜欢How to make a chain of function decorators 中的演示?更好的。
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
这是一个使用 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 。
生成 API 密钥的“典型”方法是创建 UUID(通常通过创建用户信息的某些子集 + 一些随机信息(如当前时间)的 md5 散列)。
但是,所有 API 密钥都应该是 UUID。md5创建的16进制hash符合这个要求,但肯定还有其他方法。
为用户创建密钥后,将其作为用户信息的一部分存储在数据库中,并检查他们的密钥(通常存储在 cookie 中)是否与您所拥有的相匹配。您链接到的页面中(某种程度上)描述了这种实际机制。