FastAPI实现二选一依赖注入:登录或API Key认证访问受保护路由
解决FastAPI双认证二选一的正确姿势
你之前的两种写法都存在问题:
- 第一个写法重复定义
user参数,不符合Python语法,FastAPI也无法处理同名依赖 - 第二个写法直接在路由函数内调用
Security()和Depends()是错误的,这两个是声明依赖的工具,不能直接在函数内部执行
FastAPI确实有官方推荐的多认证兼容方案,核心是自定义组合依赖,在这个依赖里依次尝试两种认证方式,只要其中一种成功就返回用户,都失败再抛出认证错误。
具体实现步骤:
- 导入必要模块:
from fastapi import Depends, HTTPException, status from fastapi_users import current_active_user from your_auth_module import get_user_from_api_key, User
- 编写自定义组合依赖函数:
async def get_current_user_or_api_key_user( user_from_auth: User = Depends(current_active_user), user_from_api_key: User = Depends(get_user_from_api_key) ): # 优先尝试用户登录认证 try: return user_from_auth except HTTPException as e: # 仅捕获认证失败的401异常,其他错误直接抛出 if e.status_code != status.HTTP_401_UNAUTHORIZED: raise e # 尝试API Key认证 try: return user_from_api_key except HTTPException: # 两种认证都失败,抛出统一的401提示 raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="请提供有效的用户认证令牌或API Key", headers={"WWW-Authenticate": "Bearer, ApiKey"}, )
注意:这里假设
current_active_user和get_user_from_api_key在认证失败时都会抛出HTTPException(401),如果你的API Key依赖抛出的异常类型不同,需要调整捕获逻辑。
- 在路由中使用这个自定义依赖:
@router.get('/protected') def wrapper_layout( layoutRun: LayoutRun, user: User = Depends(get_current_user_or_api_key_user) ): # 此处的user已通过任意一种认证方式获取,可直接用于业务逻辑 return {"user_id": user.id, "message": "访问受保护路由成功"}
如果你的两个依赖在认证失败时会返回None而非抛出异常,还可以用更简洁的写法:
async def get_current_user_or_api_key_user( user_from_auth: User | None = Depends(current_active_user), user_from_api_key: User | None = Depends(get_user_from_api_key) ): if user_from_auth: return user_from_auth if user_from_api_key: return user_from_api_key raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="请提供有效的用户认证令牌或API Key", headers={"WWW-Authenticate": "Bearer, ApiKey"}, )
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

