ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

FastAPI fastapi.security 安全工具全解:API Key、HTTP 认证与 OAuth2 依赖实现

FastAPI fastapi.security 安全工具全解:API Key、HTTP 认证与 OAuth2 依赖实现 FastAPI fastapi.security 安全工具全解API Key、HTTP 认证与 OAuth2 依赖实现【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi本篇指南以 FastAPI 官方参考文档 fastapi.security 为主体系统讲解fastapi.security模块导出的全部 15 个安全类API Key 三件套Query/Header/Cookie、HTTP Basic/Bearer/Digest 认证、HTTP 凭据模型、OAuth2 各流程、密码表单依赖、Scopes 依赖与 OpenID Connect。读完后你将掌握每个类的构造参数、默认值、错误行为auto_error语义与依赖返回值并能结合 fastapi/security/ 的源码实现理解它们如何与 OpenAPI 自动文档集成。模块总览从fastapi.security导入当你需要声明带 OAuth2 scopes 的依赖时使用Security()其定义见 param_functions.py#L2372但你仍然需要定义传入Depends()或Security()的“dependable”——即可调对象。fastapi.security提供了多种工具来创建这些依赖它们会自动集成进 OpenAPI从而出现在自动生成的文档 UI/docs中并可被自动生成的客户端和 SDK 使用。标准导入方式如下from fastapi.security import ( APIKeyCookie, APIKeyHeader, APIKeyQuery, HTTPAuthorizationCredentials, HTTPBasic, HTTPBasicCredentials, HTTPBearer, HTTPDigest, OAuth2, OAuth2AuthorizationCodeBearer, OAuth2PasswordBearer, OAuth2PasswordRequestForm, OAuth2PasswordRequestFormStrict, OpenIdConnect, SecurityScopes, )上述 15 个名称的导出清单与 fastapi/security/init.py 中的from ... import ... as ...语句一一对应该模块文件也确认了每个类的实际来源文件类实现文件分组APIKeyCookie/APIKeyHeader/APIKeyQueryfastapi/security/api_key.pyAPI KeyHTTPAuthorizationCredentials/HTTPBasic/HTTPBasicCredentials/HTTPBearer/HTTPDigestfastapi/security/http.pyHTTP 认证与凭据OAuth2/OAuth2AuthorizationCodeBearer/OAuth2PasswordBearer/OAuth2PasswordRequestForm/OAuth2PasswordRequestFormStrict/SecurityScopesfastapi/security/oauth2.pyOAuth2OpenIdConnectfastapi/security/open_id_connect_url.pyOpenID Connect所有认证类共享一个极简基类 SecurityBaseclass SecurityBase: model: SecurityBaseModel scheme_name: str从源码结构看model字段保存的是对应 OpenAPI 安全方案模型SecurityBaseModel即该依赖会出现在 OpenAPI 的components.securitySchemes中的那份声明scheme_name则是 OpenAPI 中该安全方案的名称默认为类名各类__init__中都有self.scheme_name scheme_name or self.__class__.__name__。还有一个横切所有类的关键参数auto_error默认True默认为True时若认证信息header、cookie、query 参数等未提供依赖会直接抛出 401 并中断请求设为False时认证信息缺失不会报错而是让依赖返回值为None。官方文档说明这在两种场景下有用实现可选认证未认证时走匿名逻辑以及多通道认证例如凭证可以放在 header也可以放在 HTTP Bearer token 中二者都允许缺失由业务代码自行组合判断。API Key 安全方案APIKeyQuery、APIKeyHeader、APIKeyCookie三个 API Key 类共享基类APIKeyBasefastapi/security/api_key.py#L11-L52。它们都定义“API key 应该从请求的哪个位置、以什么名称提供”并将其集成进 OpenAPI 文档它们会自动提取请求中携带的 key 值并作为依赖结果一个字符串提供给路径操作函数。注意它们只负责提取不定义如何把 key 下发给客户端也不校验 key 是否有效——有效性判断留给你的业务代码。共同参数与行为参数类型默认说明namestr必填查询参数名 / 请求头名 / Cookie 名按具体类而定scheme_namestr \| NoneNoneOpenAPI 中的安全方案名默认为类名如APIKeyHeaderdescriptionstr \| NoneNoneOpenAPI 中展示的安全方案描述auto_errorboolTrue缺失 key 时是否抛 401False时依赖返回NoneAPIKeyBase的核心校验逻辑在 check_api_keydef check_api_key(self, api_key: str | None) - str | None: if not api_key: if self.auto_error: raise self.make_not_authenticated_error() return None return api_key另外API Key 的 401 挑战头并非标准定义但 HTTP 规范RFC 9110要求 401 响应必须携带WWW-Authenticate头因此 FastAPI 在 make_not_authenticated_error 中发送自定义挑战WWW-Authenticate: APIKey。APIKeyQuery从查询参数提取 key每个__call__实现为request.query_params.get(self.model.name)api_key.py#L142-L144from fastapi import Depends, FastAPI from fastapi.security import APIKeyQuery app FastAPI() query_scheme APIKeyQuery(nameapi_key) app.get(/items/) async def read_items(api_key: str Depends(query_scheme)): return {api_key: api_key}APIKeyHeader从请求头提取 key实现为request.headers.get(self.model.name)api_key.py#L230-L232from fastapi import Depends, FastAPI from fastapi.security import APIKeyHeader app FastAPI() header_scheme APIKeyHeader(namex-key) app.get(/items/) async def read_items(key: str Depends(header_scheme)): return {key: key}APIKeyCookie从 Cookie 提取 key实现为request.cookies.get(self.model.name)api_key.py#L318-L320from fastapi import Depends, FastAPI from fastapi.security import APIKeyCookie app FastAPI() cookie_scheme APIKeyCookie(namesession) app.get(/items/) async def read_items(session: str Depends(cookie_scheme)): return {session: session}HTTP 认证方案HTTPBasic、HTTPBearer、HTTPDigest这三个类都继承自 HTTPBase其通用流程是读取Authorization请求头 → 用 get_authorization_scheme_param 按第一个空格拆分为scheme与credentials→ 缺失或不符合时按auto_error抛 401 或返回None。HTTPBase的 401 挑战头由 make_authenticate_headers 生成形如WWW-Authenticate: Scheme如Bearer。HTTPBasic对应 RFC 7617 的 HTTP Basic 认证。额外参数realm: str | None默认None认证域设置后 401 响应头为WWW-Authenticate: Basic realm...http.py#L197-L200否则仅为Basic其余参数scheme_name、description、auto_error与上文相同依赖结果为一个HTTPBasicCredentials对象包含username和password。HTTPBasic.__call__的完整实现http.py#L202-L219展示了校验细节要求 scheme 为basic然后对参数做 Base64 解码并按第一个冒号切分用户名与密码任何解码失败ValueError、UnicodeDecodeError、binascii.Error或缺少冒号都会触发 401。from typing import Annotated from fastapi import Depends, FastAPI from fastapi.security import HTTPBasic, HTTPBasicCredentials app FastAPI() security HTTPBasic() app.get(/users/me) def read_current_user(credentials: Annotated[HTTPBasicCredentials, Depends(security)]): return {username: credentials.username, password: credentials.password}HTTPBearerHTTP Bearer token 认证。额外参数bearerFormat: str | None默认None描述 Bearer token 的格式写入 OpenAPI 的bearerFormat字段用于文档提示http.py#L254-L299依赖结果为一个HTTPAuthorizationCredentials对象包含scheme和credentials。HTTPBearer.__call__在通用解析之上额外校验 scheme 必须为bearer大小写不敏感见 http.py#L311-L316否则同样走auto_error分支。from typing import Annotated from fastapi import Depends, FastAPI from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer app FastAPI() security HTTPBearer() app.get(/users/me) def read_current_user( credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)] ): return {scheme: credentials.scheme, credentials: credentials.credentials}HTTPDigest桩实现对应 RFC 7616。源码文档字符串中明确警告http.py#L320-L355HTTPDigest目前只是一个把组件与 FastAPI 的 OpenAPI 打通的桩stub并没有实现完整的 Digest 认证流程你只需继承它并在自己的代码中实现__call__逻辑。其__call__当前仅校验 Authorization 头存在且 scheme 为digest返回HTTPAuthorizationCredentials。from typing import Annotated from fastapi import Depends, FastAPI from fastapi.security import HTTPAuthorizationCredentials, HTTPDigest app FastAPI() security HTTPDigest() app.get(/users/me) def read_current_user( credentials: Annotated[HTTPAuthorizationCredentials, Depends(security)] ): return {scheme: credentials.scheme, credentials: credentials.credentials}HTTP 凭据模型HTTPAuthorizationCredentials与HTTPBasicCredentials这两个是 Pydantic 模型定义于 fastapi/security/http.py作为上述 HTTP 认证依赖的返回值类型方便你在依赖函数签名中做类型标注。HTTPAuthorizationCredentials用于HTTPBearer和HTTPDigest的依赖结果。Authorization头的值按第一个空格拆分前半部分是scheme后半部分是credentialshttp.py#L29-L66class HTTPAuthorizationCredentials(BaseModel): scheme: str # 例如 Bearer credentials: str # 例如 deadbeef12346例如客户端发送Authorization: Bearer deadbeef12346时scheme为Bearercredentials为deadbeef12346。HTTPBasicCredentials用于HTTPBasic的依赖结果http.py#L16-L26class HTTPBasicCredentials(BaseModel): username: str # HTTP Basic 用户名 password: str # HTTP Basic 密码OAuth2 认证OAuth2、OAuth2PasswordBearer、OAuth2AuthorizationCodeBearer三个类都继承自 OAuth2其构造参数包括参数类型默认说明flowsOAuthFlowsModel \| dict空OAuthFlowsModel()OAuth2 flows 字典决定写入 OpenAPI 的oauthFlows内容scheme_namestr \| NoneNoneOpenAPI 中的安全方案名默认为类名descriptionstr \| NoneNoneOpenAPI 中的安全方案描述auto_errorboolTrue无Authorization头时是否抛 401OAuth2.__call__的实现很简单oauth2.py#L423-L430只检查Authorization头是否存在存在则原样返回整个头的字符串由你的依赖函数自行解析。401 挑战头方面由于 OAuth2 规范本身没有定义统一的挑战Bearer 并非唯一选项FastAPI 出于实用考虑默认发送WWW-Authenticate: Beareroauth2.py#L401-L421如果你实现了非 Bearer 的 OAuth2 方案可以覆盖make_not_authenticated_error()。一般不应直接实例化OAuth2而是使用下面两个针对具体流程的子类如需支持多种流程也可以组合使用。OAuth2PasswordBearer用密码password flow换取 Bearer token 的 OAuth2 流程是最常用的“简单 OAuth2 Bearer”方案。参数类型默认说明tokenUrlstr必填获取 token 的 URL即使用OAuth2PasswordRequestForm作为依赖的那个路径操作scopesdict[str, str] \| NoneNone使用此依赖的路径操作所需的 OAuth2 scopes写入 OpenAPIrefreshUrlstr \| NoneNone刷新 token 获取新 token 的 URLscheme_name/description/auto_error同上同上同上OAuth2PasswordBearer会构造OAuthFlowsModel(password{tokenUrl: ..., refreshUrl: ..., scopes: ...})传给父类oauth2.py#L517-L534。其__call__与HTTPBearer类似解析 Authorization 头要求 scheme 为bearer返回token 字符串本身而不是 credentials 对象。OAuth2AuthorizationCodeBearer用 OAuth2 授权码authorization code flow换取 Bearer token 的流程。除公共参数外要求authorizationUrl: str必填授权 URLtokenUrl: str必填获取 token 的 URLrefreshUrl: str | None、scopes: dict[str, str] | None可选。它会构造OAuthFlowsModel(authorizationCode{...})oauth2.py#L622-L640__call__行为与OAuth2PasswordBearer一致校验bearerscheme 后返回 token 字符串。OAuth2 密码表单OAuth2PasswordRequestForm与OAuth2PasswordRequestFormStrict这两个是“表单数据收集”依赖类OAuth2 规范规定 password flow 的数据必须以表单form data而非 JSON提交且字段名必须精确为username和passwordoauth2.py#L14-L159。所有初始化参数都从请求中提取。字段一览表单字段类型默认说明grant_typestr \| None宽松版/str严格版宽松版可为NoneOAuth2 规范要求必填且必须为固定字符串password源码中标注了Form(pattern^password$)宽松版允许不传严格版强制要求usernamestr必填规范要求的精确字段名passwordstr必填规范要求的精确字段名OpenAPI 中标记为format: passwordscopestr以空格分隔的多个 scope 字符串如items:read items:write users:read profile openid构造时被scope.split()切成列表存放在self.scopesclient_idstr \| NoneNone可作为表单字段提交但规范推荐用 HTTP Basic 认证方式发送client_idclient_secretstr \| NoneNone同上推荐经 HTTP Basic 认证发送使用方式与 docs/en/docs/tutorial/security/simple-oauth2.md 教程一致from typing import Annotated from fastapi import Depends, FastAPI from fastapi.security import OAuth2PasswordRequestForm app FastAPI() app.post(/login) def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]): data {} data[scopes] [] for scope in form_data.scopes: data[scopes].append(scope) if form_data.client_id: data[client_id] form_data.client_id if form_data.client_secret: data[client_secret] form_data.client_secret return dataOAuth2PasswordRequestFormStrictoauth2.py#L162-L327与宽松版的唯一区别是要求客户端必须提交grant_typepassword这个表单字段严格版中grant_type: str为必填参数仍带^password$正则约束如果你的客户端确实遵循规范就选严格版兼容旧客户端则用宽松版。关于 scopes 的一点规范细节items:read是一个不透明字符串形式的单个 scope而不是两个把它按冒号拆成items与read是应用层面的约定许多应用这样组织权限并不属于 OAuth2 规范的一部分。依赖中的 OAuth2 ScopesSecurityScopesSecurityScopes是一个特殊类可以在依赖的参数中声明用于一次性拿到同一依赖链上所有依赖所要求的 OAuth2 scopesoauth2.py#L653-L691class SecurityScopes: def __init__(self, scopes: list[str] | None None): self.scopes: list[str] scopes or [] # 所有依赖要求的 scope 列表 self.scope_str: str .join(self.scopes) # OAuth2 规定的空格分隔单字符串两个属性scopes依赖链中所有依赖要求的 scope 列表由 FastAPI 在运行时填充scope_str按 OAuth2 规范以空格拼接的单个字符串。这样即使同一个路径操作中多个子依赖分别要求不同的 scopes也能在一个统一的地方访问到全部 scopes并据此做权限判断。OpenID ConnectOpenIdConnect构造参数openIdConnectUrl必填OpenID Connect URL、scheme_name、description、auto_error默认True行为同上open_id_connect_url.py#L22-L94。源码文档字符串中明确警告这同样只是一个与 OpenAPI 打通的桩——它没有实现完整的 OpenID Connect 流程例如并没有真正使用openIdConnectUrl。其__call__目前仅检查Authorization头是否存在存在则原样返回。如需完整实现需要继承该类并在自己的代码中补齐例如拉取 OIDC 元数据、校验 JWT 等。测试验证行为与 OpenAPI 集成的回归保障tests/目录下有大量与本文各主题对应的测试文件可以作为上述行为的可执行验证依据API Key 三类test_security_api_key_query.py、test_security_api_key_header.py、test_security_api_key_cookie.py各自另有_optional、_description变体验证auto_errorFalse与description写入 OpenAPIHTTP 认证test_security_http_basic_realm.pyrealm进入 401 头、test_security_http_bearer.py、test_security_http_digest.pyOAuth2test_security_oauth2.py、test_security_oauth2_authorization_code_bearer.py、test_security_oauth2_authorization_code_bearer_scopes_openapi.pyscopes 进入 OpenAPIScopes 依赖test_security_scopes_sub_dependency.py 与 test_security_scopes_dont_propagate.py分别覆盖 scopes 沿依赖链的汇总与“不向上传播”的边界依赖覆盖与安全依赖模型test_dependency_security_overrides.py测试中用app.dependency_overrides替换真实安全依赖方便测试鉴权路径。小结按场景选型场景推荐工具依赖返回值简单 API 密钥查询参数 / 请求头 / CookieAPIKeyQuery/APIKeyHeader/APIKeyCookiestr \| Nonekey 字符串账号密码HTTP 标准 BasicHTTPBasicHTTPBasicCredentials任意 Bearer token含 JWTHTTPBearerHTTPAuthorizationCredentials自建完整 OIDC / Digest 流程继承OpenIdConnect/HTTPDigest桩实现自定义规范化的 OAuth2 password 流程OAuth2PasswordRequestForm(Strict) OAuth2PasswordBearer表单对象 / tokenstr授权码流程OAuth2AuthorizationCodeBearertokenstr在依赖中统一读取所需 scopesSecurityScopesscopes列表 /scope_str所有工具的共同点通过Depends()/Security()声明、自动写入 OpenAPI 的securitySchemes与oauthFlows、支持auto_error控制可选认证、且均可被生成的客户端/SDK 理解——这正是参考文档 Security Tools 所强调的核心价值。【免费下载链接】fastapiFastAPI framework, high performance, easy to learn, fast to code, ready for production项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表