终于完成apiflask对`MultiAuth`的支持了 🫡

终于完成apiflask对MultiAuth的支持了 :saluting_face:

改动比较大,欢迎大家Code Review和提建议。

主要的改动有以下几点:
①将API Keys认证从Token认证里分离出来了
新版本:

auth = HTTPAPIKeyAuth()

旧版本:

auth = HTTPTokenAuth('apiKey', header='X-API-Key')

之前API Keys认证使用HTTPTokenAuth的原因是:它的底层实现实际上是flask-httpauth的HTTPTokenAuth
我查看了一下OpenAPI文档和其他资料,实际上API Keys可以位于查询参数(query parameters)、请求头或者cookie中。但是由于目前apiflask是使用的HTTPTokenAuth来实现API Keys认证,所以暂时只支持位于请求头中。
出于个人需求,apiflask后续可能会支持cookie认证。所以我先把它分离出来,为下一步的完整支持做准备。
旧用法仍然被兼容,但是会产生一个deprecation warning。

②把flask-httpauth的最低版本升级到了4.2.0
因为要支持MultiAuthoptional参数,所以flask-httpauth的最低版本被提升到了4.2.0。
在这个改动后,部分依赖版本不支持Python 3.8/3.9了…
如果不支持这个两个低版本,会对大家的使用造成影响,我建议这个功能等到3.0版本再发布

③security scheme的命名规则
修改当前的security scheme命名规则将会是破坏性的。所以我并没有实施,但是想在这里和大家讨论一下这个设计问题。
在当前版本中,如果security scheme的命名重复,apiflask会为其自动添加一个递增的后缀。
例如,当有两个认证类的security scheme被同时命名为"BasicAuth"时,在最终生成的OpenAPI文档中,第一个认证类的security schema名称是BasicAuth,第二个认证类的security scheme名称会被apiflask自动添加上一个后缀,被重命名为BasicAuth_2

auth = HTTPBasicAuth(security_scheme_name='BasicAuth')       # BasicAuth
auth2 = HTTPBasicAuth(security_scheme_name='BasicAuth')      # BasicAuth_2

我认为目前的命名规则并不好:

  • 擅自修改了用户输入:在用户不知情的情况下,修改了原本的security scheme名称
  • 违背了清晰原则:“BasicAuth_2”这样的命名并不好,只是简单地回避了命名冲突,没有提供其他有效信息
  • 增加了代码复杂性:代码实现需要检查命名是否冲突,并递推后缀(说实话,阅读和修改这段代码的时候有点痛苦… :joy:

目前我想到了两个方案:

  • 在命名冲突时,产生警告或异常
  • 后声明的命名覆盖掉先声明的命名

第二种方案参考自django-ninja
django-ninja的security schema名称是直接使用的认证类的类名。例如,下面这个示例(不良示例,请勿模仿)中两个认证类的名称都为“APIKey”,但是一个位于查询参数中,一个位于请求头中。根据Python规则,后定义的类会覆盖先定义的类。所以最后生成的OpenAPI文档中,API Key会位于请求头中。

class APIKey(AuthCheck, APIKeyQuery):
    pass


class APIKey(AuthCheck, APIKeyHeader):
    pass


@api.get("/multiple", auth=[APIKey(), APIKey()])
def multiple(request):
    return f"Token = {request.auth}"
2 个赞