我有一个 FastAPI 应用程序,我通过注入依赖函数来启用
Authentication
。
控制器.py
router = APIRouter(
prefix="/v2/test",
tags=["helloWorld"],
dependencies=[Depends(api_key)],
responses={404: {"description": "Not found"}},
)
授权.py
async def api_key(api_key_header: str = Security(api_key_header_auth)):
if api_key_header != API_KEY:
raise HTTPException(
status_code=401,
detail="Invalid API Key",
)
这很好用。但是,我想disable 基于环境的身份验证。例如,我想在
localhost
环境中继续输入身份验证密钥。
APIKeyHeader
类的子类并覆盖 __call__()
方法来执行检查请求是否来自“安全”client
,例如 localhost
或 127.0.0.1
,使用 request.client.host
,如here所述。如果是这样,您可以将 api_key
设置为应用程序的 API_KEY
值,check_api_key()
依赖函数将返回并使用该值来验证 api_key
。如果没有特定于应用程序的 API_KEY
,而是多个 api 密钥,则可以在 __call__()
和 check_api_key()
函数中对客户端的请求来源执行检查,并且仅当客户端的主机名不在safe_clients
列表。
from fastapi import FastAPI, Request, Depends, HTTPException
from starlette.status import HTTP_403_FORBIDDEN
from fastapi.security.api_key import APIKeyHeader
from fastapi import Security
from typing import Optional
API_KEY = 'some-api-key'
API_KEY_NAME = 'Authorization'
safe_clients = ['127.0.0.1']
class MyAPIKeyHeader(APIKeyHeader):
async def __call__(self, request: Request) -> Optional[str]:
if request.client.host in safe_clients:
api_key = API_KEY
else:
api_key = request.headers.get(self.model.name)
if not api_key:
if self.auto_error:
raise HTTPException(
status_code=HTTP_403_FORBIDDEN, detail='Not authenticated'
)
else:
return None
return api_key
api_key_header_auth = MyAPIKeyHeader(name=API_KEY_NAME)
async def check_api_key(request: Request, api_key: str = Security(api_key_header_auth)):
if api_key != API_KEY:
raise HTTPException(
status_code=401,
detail='Invalid API Key',
)
app = FastAPI(dependencies=[Depends(check_api_key)])
@app.get('/')
def main(request: Request):
return request.client.host
Authorize
按钮上面提供的示例将按预期工作,即 IP 地址包含在
safe_clients
列表中的用户将不会被要求提供 API 密钥以向 API 发出请求,无论Authorize
在
/docs
处访问自动文档时,按钮出现在 Swagger UI 页面中。但是,如果您仍然想从
Authorize
的 UI 中删除 safe_clients
按钮,您可以使用自定义中间件,如 here 所示执行一些检查,以便从中删除 securitySchemes
组件OpenAPI 模式(在 /openapi.json
中)——Swagger UI 实际上是基于 OpenAPI 规范的。这种方法的灵感来自前面提到的链接,以及here和here。请确保在上面的示例中初始化您的应用程序后添加中间件(即afterapp = FastAPI(dependencies=...)
)
from fastapi import Response
# ... rest of the code is the same as above
app = FastAPI(dependencies=[Depends(check_api_key)])
@app.middleware("http")
async def remove_auth_btn(request: Request, call_next):
response = await call_next(request)
if request.url.path == '/openapi.json' and request.client.host in safe_clients:
response_body = [section async for section in response.body_iterator]
resp_str = response_body[0].decode() # convert "response_body" bytes into string
resp_dict = json.loads(resp_str) # convert "resp_str" into dict
del resp_dict['components']['securitySchemes'] # remove securitySchemes
resp_str = json.dumps(resp_dict) # convert "resp_dict" back to str
return Response(content=resp_str, status_code=response.status_code, media_type=response.media_type)
return response