- 后端
- 认证鉴权
- Web框架
【免费下载链接】fastapi-users
Ready-to-use and customizable users management for FastAPI
导读
本文基于 FastAPI Users 官方迁移文档(docs/migration/8x_to_9x.md),系统讲解从 8.x 升级到 9.x 时认证体系的核心变化:原先"一个后端类同时负责令牌生成与传输"的模型,被拆分为Transport(传输层)与Strategy(策略层)两个独立概念,并通过AuthenticationBackend组合。读完本文,你将掌握如何将旧的JWTAuthentication/CookieAuthentication代码迁移为新式写法、如何为每个认证后端生成独立的 OAuth 路由,以及迁移后的登录/登出底层调用链与 OpenAPI 响应差异。
为什么 9.x 要拆分认证后端
在 8.x 版本中,一个认证后端(如JWTAuthentication、CookieAuthentication)同时承担两件事:
- 决定令牌如何生成与安全校验;
- 决定令牌如何随请求传输(放在
Authorization头,还是写入 Cookie)。
9.x 将其拆成两个正交的抽象(见 fastapi_users/authentication/transport/base.py 与 fastapi_users/authentication/strategy/base.py):
- Transport(传输层):令牌如何随请求携带,对应
BearerTransport、CookieTransport; - Strategy(策略层):令牌如何生成与校验,对应
JWTStrategy,未来可扩展数据库会话令牌等新策略。
拆分的直接收益正如迁移文档所言:"我们很快就能提供数据库会话令牌之类的新策略,而无需重复编写完全相同的传输逻辑"——传输逻辑与令牌生成逻辑从此可以独立演进、任意组合。迁移文档原文见 docs/migration/8x_to_9x.md。
新架构三件套:AuthenticationBackend+ Transport + Strategy
迁移后的核心组合类为AuthenticationBackend,其定义位于 fastapi_users/authentication/backend.py:
class AuthenticationBackend(Generic[models.UP, models.ID]): def __init__( self, name: str, transport: Transport, get_strategy: DependencyCallable[Strategy[models.UP, models.ID]], ): self.name = name self.transport = transport self.get_strategy = get_strategy三个参数含义(对照 docs/configuration/authentication/backend.md):
name(str):后端唯一名称,9.x 起不再有默认值,必须自行提供;transport:一个Transport实例,负责令牌的携带方式;get_strategy:一个返回Strategy实例的依赖可调用对象(Callable)。之所以要求"函数"而非直接传实例,是为了让策略能够随依赖动态实例化(详见 docs/configuration/authentication/strategies/jwt.md 中的说明)。
AuthenticationBackend将两者编排成完整的认证流程(backend.py):
- login:
strategy.write_token(user)生成令牌 →transport.get_login_response(token)返回携带令牌的 HTTP 响应; - logout:调用
strategy.destroy_token(token, user)尝试注销令牌,若策略不支持销毁则静默跳过(StrategyDestroyNotSupportedError);随后调用transport.get_logout_response(),若传输层不支持登出响应则返回204 No Content。
从JWTAuthentication迁移
迁移前(8.x)
from fastapi_users.authentication import JWTAuthentication jwt_authentication = JWTAuthentication( secret=SECRET, lifetime_seconds=3600, tokenUrl="auth/jwt/login" )迁移后(9.x)
from fastapi_users.authentication import AuthenticationBackend, BearerTransport, JWTStrategy SECRET = "SECRET" bearer_transport = BearerTransport(tokenUrl="auth/jwt/login") def get_jwt_strategy() -> JWTStrategy: return JWTStrategy(secret=SECRET, lifetime_seconds=3600) auth_backend = AuthenticationBackend( name="jwt", transport=bearer_transport, get_strategy=get_jwt_strategy, )迁移要点:
tokenUrl从JWTAuthentication移交给BearerTransport构造参数(bearer.py 中通过OAuth2PasswordBearer(tokenUrl, auto_error=False)构造 FastAPI 安全依赖);secret、lifetime_seconds移交给JWTStrategy;- 必须为后端提供
name(迁移文档中的红色警告,docs/migration/8x_to_9x.md); - 登录成功后
BearerTransport返回200 OK的 JSON,体为{"access_token": "...", "token_type": "bearer"}(对应 BearerResponse)。
从CookieAuthentication迁移
迁移前(8.x)
from fastapi_users.authentication import CookieAuthentication cookie_authentication = CookieAuthentication(secret=SECRET, lifetime_seconds=3600)迁移后(9.x)
from fastapi_users.authentication import AuthenticationBackend, CookieTransport, JWTStrategy SECRET = "SECRET" cookie_transport = CookieTransport(cookie_max_age=3600) def get_jwt_strategy() -> JWTStrategy: return JWTStrategy(secret=SECRET, lifetime_seconds=3600) auth_backend = AuthenticationBackend( name="cookie", transport=cookie_transport, get_strategy=get_jwt_strategy, )迁移要点:
- 旧代码里
lifetime_seconds=3600同时控制令牌有效期与 Cookie 存活期;迁移后两者被拆分——令牌有效期由JWTStrategy(lifetime_seconds=3600)控制,Cookie 存活期由CookieTransport(cookie_max_age=3600)控制(cookie.py); CookieTransport支持更细粒度的 Cookie 参数:cookie_name(默认"fastapiusersauth")、cookie_path(默认/)、cookie_domain、cookie_secure(默认True)、cookie_httponly(默认True)、cookie_samesite("lax"/"strict"/"none",默认"lax")。登录时通过response.set_cookie(...)写入令牌,登出时以空值与max_age=0清除(cookie.py);- 与 Bearer 不同,Cookie 登录/登出响应均为
204 No Content,OpenAPI 文档中同样以204建模(cookie.py)。
迁移文档特别强调:两个示例中的get_jwt_strategy完全一致——这正是"令牌生成与传输解耦"的直观体现:同一套 JWT 策略,可以既用于 Bearer 头,也用于 Cookie。
登录响应差异速查
| 传输层 | 登录成功响应 | 登出响应 | 令牌携带方式 |
|---|---|---|---|
BearerTransport | 200 OK,JSON:{"access_token": "...", "token_type": "bearer"} | 204 No Content(由后端兜底返回) | Authorization: Bearer <token> |
CookieTransport | 204 No Content+Set-Cookie | 204 No Content+ 清除 Cookie | Cookie: fastapiusersauth=<token> |
依据:BearerTransport.get_logout_response()会抛出TransportLogoutNotSupportedError(bearer.py),此时AuthenticationBackend.logout会捕获该异常并返回204(backend.py);该行为由 tests/test_authentication_backend.py 中的test_logout用例验证。
多个后端与JWTStrategy细节
一个应用,多个认证后端
新架构允许你自由组合:既可以用多个 transport 搭配同一个JWTStrategy,也可以为不同后端提供不同策略(例如日后引入的数据库会话令牌)。只需为每个组合生成一个AuthenticationBackend,再逐一传入FastAPIUsers实例并为其生成认证路由。更完整的参数说明可参考 docs/configuration/authentication/backend.md 与 docs/configuration/authentication/strategies/jwt.md。
JWTStrategy构造参数
从源码(strategy/jwt.py)与 jwt 策略文档 可以看到:
secret:签名密钥,务必使用强口令并妥善保管;lifetime_seconds(Optional[int]):令牌有效秒数;设为None则永不过期,存在严重安全隐患;token_audience(Optional[list[str]]):JWT 合法受众,默认["fastapi-users:auth"];algorithm(Optional[str]):JWT 加密算法,默认"HS256";需要非对称密钥时改用"RS256";public_key:使用 RSA 等非对称算法时提供解密公钥,secret始终用于加密。
write_token将用户 ID 写入sub声明并生成令牌;read_token校验签名与受众后通过user_manager.parse_id+user_manager.get还原用户(strategy/jwt.py)。由于 JWT 天然无状态,"登出即失效"无法实现,destroy_token会抛出JWTStrategyDestroyNotSupportedError,交由后端静默忽略(strategy/jwt.py),这也是 jwt 策略文档 中"Logout 什么都不做"的源码依据。
OAuth:一个后端一个路由
迁移前(8.x)
8.x 中,单个 OAuth 路由即可配合任意一个认证后端工作:
app.include_router( fastapi_users.get_oauth_router(google_oauth_client, "SECRET"), prefix="/auth/google", tags=["auth"], )迁移后(9.x)
现在必须为每个认证后端生成独立的 OAuth 路由,将auth_backend作为第二个位置参数传入:
app.include_router( fastapi_users.get_oauth_router(google_oauth_client, auth_backend, "SECRET"), prefix="/auth/google", tags=["auth"], )迁移文档明确指出:"现在,你需要为你的每一个后端分别生成一个路由。" 如果你有多个 OAuth 客户端和/或多个认证后端,就需为每一对组合创建路由(同 docs/configuration/oauth.md 的说明)。
从源码看(router/oauth.py),get_oauth_router在 9.x 中的签名确实加入了backend: AuthenticationBackend参数,且回调路由名变为oauth:{client.name}.{backend.name}.callback——不同后端对应的 callback 路由由此天然区分。OAuth 回调端点通过Depends(backend.get_strategy)注入策略,并以backend.login(strategy, user)完成最终登录(router/oauth.py)。
/authorize不再需要authentication_backend参数
迁移的一个直接结果是:请求/authorize时不再需要指定authentication_backend查询参数。
迁移前:
curl \ -H "Content-Type: application/json" \ -X GET \ http://localhost:8000/auth/google/authorize?authentication_backend=jwt迁移后:
curl \ -H "Content-Type: application/json" \ -X GET \ http://localhost:8000/auth/google/authorize原因很自然:既然每个 OAuth 路由现在已与唯一的AuthenticationBackend绑定(在生成路由时指定),"用哪个后端登录"就不必再由客户端在请求时声明。/authorize端点仅返回授权跳转 URL(OAuth2AuthorizeResponse.authorization_url),真正与后端交互发生在/callback(router/oauth.py)。
迁移检查清单
- 删除
JWTAuthentication/CookieAuthentication的实例化代码; - 分别实例化
BearerTransport(tokenUrl=...)或CookieTransport(cookie_max_age=...); - 定义
get_jwt_strategy()返回JWTStrategy(secret=..., lifetime_seconds=...); - 用
AuthenticationBackend(name=..., transport=..., get_strategy=...)组合三者,务必提供唯一name; - 将后端列表传入
FastAPIUsers实例,并为其逐一生成 auth 路由; - OAuth 路由改为
get_oauth_router(client, auth_backend, "SECRET"),每后端一个; - 移除请求
/authorize时携带的authentication_backend参数。
迁移后到哪里看完整示例
迁移文档在结尾提示:"如果你不确定或有些迷茫,务必查看完整可运行示例"(docs/migration/8x_to_9x.md)。仓库中提供了可直接对照的完整工程:
- 认证与传输配置总览:docs/configuration/full-example.md
- SQLAlchemy + Bearer/JWT 示例:examples/sqlalchemy/app/app.py、examples/sqlalchemy/app/users.py
- Beanie + Bearer/JWT 示例:examples/beanie/app/app.py、examples/beanie/app/users.py
- SQLAlchemy + OAuth 示例:examples/sqlalchemy-oauth/app/app.py
- Beanie + OAuth 示例:examples/beanie-oauth/app/app.py
这些示例均已在 9.x 新架构下编写,迁移时可直接对照其中的AuthenticationBackend组装方式与 OAuth 路由注册方式。
- 后端
- 认证鉴权
- Web框架
【免费下载链接】fastapi-users
Ready-to-use and customizable users management for FastAPI
相关推荐
FastAPI-Users 从 8.x 到 9.x 版本迁移指南:认证架构的重大革新
FastAPI Users 从 8.x 到 9.x 版本迁移指南:认证架构的重大革新 前言 FastAPI Users 作为 FastAPI 生态中优秀的用户认
后端认证鉴权Web框架深入解析 FastAPI Users 认证后端:用 AuthenticationBackend 组合 Transport 与 Strategy
深入解析 FastAPI Users 认证后端:用 AuthenticationBackend 组合 Transport 与 Strategy 本文围绕 Fas
后端认证鉴权Web框架FastAPI Users 认证体系完全指南:Transport + Strategy 组合式认证后端详解
FastAPI Users 认证体系完全指南:Transport + Strategy 组合式认证后端详解 导读 本文是 FastAPI Users 认证体系的
后端认证鉴权Web框架
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考