推荐用HTTPBearer提取Bearer格式API Key,若用X-API-Key等自定义Header则须手写依赖函数;HTTPBearer仅提取不校验,校验需自行实现,且注意反向代理透传和Header大小写问题。

FastAPI 中用 HTTPBearer 提取 API Key 最直接
不推荐手写依赖去解析 Authorization Header,FastAPI 官方的 HTTPBearer 已经封装好标准流程:自动检查 Header 是否存在、是否以 "Bearer " 开头、提取 token 字符串。它本质是帮你省掉字符串切分和空值判断这类重复逻辑。
关键点:
-
HTTPBearer默认要求 Header 是Authorization: Bearer xxx格式,不是API-Key或其他自定义 key —— 如果你已有客户端固定发X-API-Key,得换方案 - 它只做提取,不做校验;后续需自行比对密钥是否合法
- 若想支持
API-Key: xxx这类非 Bearer 格式,必须自己写依赖函数,不能复用HTTPBearer
自定义 Header 认证(如 X-API-Key)必须手写依赖
当你的前端或第三方调用约定用 X-API-Key 而非 Authorization 时,HTTPBearer 就失效了。这时要写一个普通依赖函数,用 Request 对象手动取 Header。
示例代码片段:
from fastapi import Depends, HTTPException, Request from starlette.status import HTTP_403_FORBIDDENasync def verify_api_key(request: Request): api_key = request.headers.get("X-API-Key") if not api_key: raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="X-API-Key header missing") if api_key != "your-secret-key-here": # 实际中建议查数据库或缓存 raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="Invalid API Key") return api_key
注意:
- 别把密钥硬编码在代码里,应从环境变量读取:
os.getenv("API_KEY") - 生产环境务必用更安全的存储方式(如 Vault、KMS),且避免明文比对,考虑加盐哈希或 token 签名验证
- 这个函数返回值会注入到路由参数中,可选地用于日志或审计
如何在路由中应用 API Key 验证
把上面写的依赖函数传给 Depends(),就能保护单个接口或整个 router。
常见用法:
- 保护单个接口:
def read_items(api_key: str = Depends(verify_api_key)): - 保护整个 router:
router = APIRouter(dependencies=[Depends(verify_api_key)]) - 组合多个认证(比如部分接口允许 API Key,部分需要 OAuth2):用
Union或分别定义不同依赖,但 FastAPI 不自动“或”逻辑,需手动写分支
错误响应默认是 403,如果你希望统一返回 401(Unauthorized),需显式设置 status_code=401 并调整 WWW-Authenticate Header,否则 OpenAPI 文档里会显示为 Forbidden。
容易忽略的部署与调试细节
本地开发时 curl 测试没问题,一上 Nginx 或 Cloudflare 就 403,大概率是反向代理默认过滤了自定义 Header。
排查要点:
- Nginx 需显式透传:
proxy_set_header X-API-Key $http_x_api_key;(注意大小写转换规则) - Cloudflare 免费版默认剥离所有非标准 Header,需升级或改用
Authorization: Bearer格式 - FastAPI 的
debug=True不会影响认证逻辑,但错误堆栈可能暴露密钥——确保生产关闭 debug - 如果用
HTTPBearer,客户端误发Authorization: API-Key xxx会导致解析失败,报错信息是Credentials not found in header,而非密钥错误
Header 名称大小写敏感,但 HTTP 协议规定 Header 名不区分大小写;实际中某些中间件(如某些 CDN)可能按字面匹配,建议统一用小写键名(x-api-key)并用 .get() 安全获取。










