首页
/ FastAPI 认证错误状态码定制指南:通过 `make_not_authenticated_error` 从 401 回退到旧版 403

FastAPI 认证错误状态码定制指南:通过 `make_not_authenticated_error` 从 401 回退到旧版 403

2026-09-07 13:52:12作者:董宙帆

本文以 FastAPI 0.122.0 引入的认证失败响应变更(403 → 401 + WWW-Authenticate)为切入点,讲解如何通过覆盖安全工具类中的 make_not_authenticated_error 方法,恢复旧版 403 Forbidden 行为以满足存量客户端兼容需求。读完本文你将掌握认证失败异常的产生链路、各类内置安全工具的默认差异,以及如何用十行以内代码完成状态码定制,并了解对应的测试验证方式。

背景:0.122.0 版本对认证失败响应的行为变更

在 FastAPI 0.122.0 版本之前,内置安全工具(HTTPBearerHTTPBasic、API Key、OAuth2 等)在认证失败并向客户端返回错误时,使用的是 403 Forbidden 这个 HTTP 状态码。

从 FastAPI 0.122.0 版本开始,内置安全工具改用语义上更贴切的 401 Unauthorized 状态码,并在响应中附带合适的 WWW-Authenticate 响应头。这一调整遵循了 HTTP 规范的相关规定:

  • 当服务端以 401 Unauthorized 拒绝请求时,必须通过 WWW-Authenticate 头告知客户端应当使用哪种认证方案;
  • 403 Forbidden 则更多表示“已识别请求者但拒绝其访问”,与“尚未提供有效凭证”的语义存在偏差。

换言之,新行为更准确地描述了“认证未通过”这一状态,而不是“认证已通过但被禁止访问”。

存量客户端的兼容问题与解决思路

行为变更本身更规范,但如果你有存量客户端依赖旧行为——例如把 403 当作“未登录需跳转登录页”的触发条件、或在后端日志/告警规则里按状态码分流——那么升级 FastAPI 后这些逻辑可能静默失效。

FastAPI 为这种情况保留了明确的定制入口:在自定义安全工具子类中覆盖 make_not_authenticated_error 方法,即可完整接管“认证失败时抛出何种异常”的决策,从而按需返回任意状态码。

完整示例:让 HTTPBearer 认证失败时返回 403

官方示例位于 docs_src/authentication_error_status_code/tutorial001_an_py310.py,它创建了 HTTPBearer 的子类 HTTPBearer403,将默认的 401 覆盖为 403

from typing import Annotated

from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer

app = FastAPI()


class HTTPBearer403(HTTPBearer):
    def make_not_authenticated_error(self) -> HTTPException:
        return HTTPException(
            status_code=status.HTTP_403_FORBIDDEN, detail="Not authenticated"
        )


CredentialsDep = Annotated[HTTPAuthorizationCredentials, Depends(HTTPBearer403())]


@app.get("/me")
def read_me(credentials: CredentialsDep):
    return {"message": "You are authenticated", "token": credentials.credentials}

这段代码的关键点:

  1. 继承而非修改HTTPBearer403(HTTPBearer) 只覆盖认证失败异常的产生逻辑,HTTPBearer 原有的凭证解析、OpenAPI 文档生成(securitySchemes 中仍是 {"type": "http", "scheme": "bearer"})等能力全部保留;
  2. 使用 Depends 注入:与直接实例化 HTTPBearer() 相同,通过 Annotated + Depends 组合声明依赖,路径操作函数中即可直接取到解析后的 HTTPAuthorizationCredentials
  3. 状态码可自由组合:示例使用 status.HTTP_403_FORBIDDEN,你也可以据此返回 status.HTTP_401_UNAUTHORIZED 之外的任何自定义语义状态码。

重要提示:返回异常实例而非抛出

官方文档特别强调了一个易错点:

该方法返回的是异常实例,而不是在方法内部抛异常。真正的抛掷动作由内部其余代码完成。

这从示例代码可以印证——方法体内只有 return HTTPException(...),没有任何 raise 语句。之所以这样设计,是为了让框架可以在不同调用路径上统一处理:无论是否启用 auto_error,认证失败都收敛到同一处“生成异常”的逻辑,而“何时抛出”由调用方决定。

源码级原理:认证失败异常是怎么产生的

默认实现位于 HTTPBase 基类

所有 HTTP 认证工具(HTTPBasicHTTPBearerHTTPDigest)都继承自 fastapi/security/http.py 中的 HTTPBase。默认的 make_not_authenticated_error 与配套的 make_authenticate_headers 实现在 http.py#L84-L92

def make_authenticate_headers(self) -> dict[str, str]:
    return {"WWW-Authenticate": f"{self.model.scheme.title()}"}

def make_not_authenticated_error(self) -> HTTPException:
    return HTTPException(
        status_code=HTTP_401_UNAUTHORIZED,
        detail="Not authenticated",
        headers=self.make_authenticate_headers(),
    )

默认行为由此一目了然:认证失败时生成一个 401 状态码、"Not authenticated" 详情的 HTTPException,并依据当前认证方案(BearerBasicDigest 等)自动带上 WWW-Authenticate 头。从源码结构看,make_authenticate_headers 被单独拆出,就是为了便于各方案定制挑战头内容。

调用时机:何时触发抛出

HTTPBearer 为例,其在 http.py#L303-L316__call__ 方法中解析 Authorization 头:

authorization = request.headers.get("Authorization")
scheme, credentials = get_authorization_scheme_param(authorization)
if not (authorization and scheme and credentials):
    if self.auto_error:
        raise self.make_not_authenticated_error()
    else:
        return None

可见触发路径集中在两类情况:

  • 凭证缺失或格式不完整(无 Authorization 头、无 scheme、无凭证);
  • scheme 不匹配(如声明了 Bearer 却收到了其他方案,http.py#L311-L315)。

HTTPBasic 还会在 base64 解码失败、缺少分隔冒号等场景下抛出同一异常(见 http.py#L212-L218)。

auto_error=False 的旁证

上述代码还揭示了一个细节:当 auto_error=False 时不会抛出异常而是返回 None。由于“生成异常”与“抛出异常”被解耦,这个开关才能干净地实现可选认证——这也正是 make_not_authenticated_error 只返回不抛出的架构价值所在。

其他内置安全工具的差异化实现

并非所有安全工具都共享 HTTPBase 的默认实现,了解它们各自的实现能帮你举一反三:

安全工具 源码位置 认证失败响应细节
HTTPBasic fastapi/security/http.py 覆盖 make_authenticate_headers,若配置了 realm 则返回 Basic realm="..."http.py#L197-L200
APIKeyBase(API Key) fastapi/security/api_key.py 401 + WWW-Authenticate: APIKey。因 HTTP 规范要求 401 必须携带该头,而 API Key 认证暂无标准挑战方案,故使用自定义挑战值 APIKeyapi_key.py#L31-L45
OAuth2(Bearer 流程) fastapi/security/oauth2.py 401 + WWW-Authenticate: Beareroauth2.py#L401-L421
OpenIdConnect fastapi/security/open_id_connect_url.py 401 + WWW-Authenticate: Beareropen_id_connect_url.py#L80-L85

从这些实现可以看出 make_not_authenticated_error 是统一而稳定的扩展点:任何继承自这些基类的子类,只需重写这一个方法即可整体改变认证失败响应的状态码与响应头,同时保留各方案自身的解析与挑战头逻辑。

测试验证:行为与 OpenAPI 双重校验

仓库中配套了针对该示例的测试用例 tests/test_tutorial/test_authentication_error_status_code/test_tutorial001.py,包含三个维度的断言:

  1. 认证成功路径:携带 Authorization: Bearer secrettoken 请求 /me 应返回 200 及解析出的 token(对应示例中 credentials.credentials 的取值逻辑);
  2. 认证失败路径:不带凭证请求 /me 应返回 403{"detail": "Not authenticated"},精确验证了子类覆盖生效;
  3. OpenAPI 文档完整性/openapi.json/mesecurity 声明引用了 HTTPBearer403,且 securitySchemes 中该方案被识别为 {"type": "http", "scheme": "bearer"}

第三点很有价值:它证明覆盖认证失败状态码不会破坏 OpenAPI 文档生成。即使类名变成了 HTTPBearer403,只要继承关系正确,FastAPI 依然能基于 HTTPBase 的模型信息自动生成正确的 Bearer 安全方案描述,交互式文档中的“Authorize”功能不受影响。

迁移与定制建议

  • 升级前先评估客户端:确认是否有代码、网关或监控把 403 硬编码为“认证失败”信号;若有,优先让客户端适配 401(新行为符合 HTTP 规范),实在无法调整时再使用本文方案回退;
  • 按安全方案分别定制:本文示例针对 HTTPBearer,如果同时使用 API Key、OAuth2 等方案,需要对每种方案的类分别创建子类并覆盖对应方法;
  • 注意 403 不应携带 WWW-Authenticate:回退到 403 时示例直接省略了 headers 参数,这是合理的——WWW-Authenticate 专属于 401 的挑战语义,挂在 403 上反而会造成语义混淆;
  • 保持 detail 一致:示例保留 "Not authenticated" 默认文案,便于存量客户端按该消息继续识别失败原因,也可按业务需要改为自己的描述。

综上所述,make_not_authenticated_error 是 FastAPI 为安全工具开放的一个小而精的扩展点:它把“认证失败后客户端应看到什么”完整交给开发者,在向后兼容与规范演进之间提供了干净的取舍空间,且几乎不影响类型安全、依赖注入与 OpenAPI 生态。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388