一、问题背景:.NET 10 的 OpenAPI 体系变了
如果你最近升级到 .NET 10,会发现 API 文档这块发生了一次"地壳运动"——
「Swashbuckle 不再是 ASP.NET Core 项目模板的默认选项了」。微软在 .NET 9 开始就推出了官方的 Microsoft.AspNetCore.OpenApi 包,到了 .NET 10 已经成为生成 OpenAPI 文档的官方标准。同时,「Scalar」 作为新一代 API 文档 UI 也越来越受欢迎,因为它更现代、更美观、还原生支持 OpenAPI 3.1。
这次升级带来一个新问题:「老的 Swashbuckle 那套 AddSwaggerGen 里配置 SecurityRequirement 的写法不能用了」。新的体系下,鉴权配置要通过 IOpenApiDocumentTransformer 来完成。
很多老教程在 .NET 10 上跑不通,就是因为没跟上这个变化。
二、方案一:JWT Bearer 手动鉴权(最简方案)
这套方案适合大多数内部项目——你已经有自己的登录接口,能颁发 JWT Token,只想让 Swagger / Scalar UI 上能输一次 Token 然后所有接口自动带上。
2.1 前提:JWT 认证配置好
先确认你的项目里 JWT Bearer 认证已经配好了:
这部分跟以前一样,没有变化。
2.2 编写 Document Transformer 注入 Security Scheme
新建一个文件 Transformers/BearerSecuritySchemeTransformer.cs:
这段代码做的事情很简单:「在 OpenAPI 文档的 components.securitySchemes 节点里注册一个名为 Bearer 的 HTTP Bearer 鉴权方案」。Swagger UI 和 Scalar UI 看到这个声明,就会自动在界面右上角渲染出 "Authorize" 按钮。
2.3 注册 Transformer
在 Program.cs 里注册:
2.4 启用 Swagger UI 或 Scalar UI
如果你用 Swagger UI(需要 Swashbuckle.AspNetCore.SwaggerUI 包):
如果你用 Scalar UI(「.NET 10 推荐选项」,需要 Scalar.AspNetCore 包):
「完全一样的 Transformer,两个 UI 都能用」。这是 OpenAPI 标准化带来的好处。
2.5 使用效果
启动项目,访问 /swagger 或 /scalar,右上角会出现 「Authorize」 按钮。点击后弹出输入框,粘贴 Token,确认。之后所有"Try it out"请求都会自动在 Header 中带上 Authorization: Bearer xxx。
「浏览器会话内持久化,刷新页面不丢」。
2.6 一个细节:可选的全局安全要求
上面的写法只声明了"系统支持 Bearer 鉴权",并没有把所有接口标记为"需要鉴权"。这样做的好处是:哪些接口需要鉴权由 [Authorize] 特性决定,不需要鉴权的接口在 UI 上也不会显示锁图标,更直观。
如果你确实想全局要求鉴权,可以在 Transformer 里加上:
「但通常不推荐」——让 [Authorize] 特性自然驱动,反而更清晰。
三、方案二:OAuth 2.0 自动跳转鉴权(企业级方案)
如果你的系统接入了 IdentityServer、Auth0、Keycloak、Azure AD 这类企业级身份服务,方案一就不够用了。你需要的是:「点击 Authorize → 自动跳转到 IdP 登录页 → 登录完跳回来 → 自动拿到 Token 并应用」。
这就是 OAuth 2.0 + PKCE 的完整流程。下面以 Duende IdentityServer 的演示实例为例。
3.1 配置 JWT Bearer 指向 IdP
Authority 指向 IdP 的根地址,框架会自动通过 /.well-known/openid-configuration 拉取签名公钥、Token 端点等元数据,「完全无需手动配置」。
3.2 在 OpenAPI 文档里声明 OAuth2 Security Scheme
「几个关键点」:
AuthorizationUrl和TokenUrl分别是 IdP 的授权端点和 Token 端点Scopes字典是 UI 上让用户勾选授权的范围document.Security列表把这套 OAuth 方案应用为全局要求
3.3 启用 Swagger UI 并打开 PKCE
「OAuthUsePkce() 这行不能漏」。PKCE(Proof Key for Code Exchange)是当前 OAuth 2.0 公共客户端的最佳实践,IdentityServer 默认要求 PKCE,少了这行整个授权流程会失败。
3.4 测试授权流程
启动项目(「注意必须 HTTPS」),打开 Swagger UI,点击右上角 「Authorize」:
弹窗里输入 client_id(Duende 演示实例用interactive.confidential)和client_secret(secret)勾选需要的 scopes 确认 flow是authorizationCode with PKCE点击 Authorize
浏览器会自动跳转到 IdP 登录页。用演示账号 bob/bob 登录,授权后会跳回来。
接下来调用任何 API,Swagger UI 会自动在请求 Header 加上 IdP 颁发的 JWT。点开 "Try it out" 看 curl 命令,能清楚看到 Authorization: Bearer eyJ...。
四、两种方案的对比和选型建议
什么时候用方案一,什么时候用方案二?我的建议是这样:
「方案一(JWT Bearer 手动)适合:」
内部管理后台、企业内网工具、原型验证、对接已有自研登录系统的场景。优点是配置简单,几十行代码就能跑通;缺点是开发者每次都要手动准备 Token,体验略差。
「方案二(OAuth 2.0 自动跳转)适合:」
对外开放的开发者平台、SaaS 产品 API、已经部署了 IdentityServer/Auth0/Keycloak 的企业。优点是开发体验丝滑,点击一次 Authorize 就能完成整个登录流程;缺点是配置稍复杂,需要 IdP 支持。
一个值得知道的事实:「两种方案可以共存」。在同一个 OpenAPI 文档里同时声明 Bearer 和 oauth2 两个 Security Scheme 是合法的,UI 会让用户自由选择。这对那些既要给外部开发者用、也要给内部测试用的项目特别友好。
五、生产环境的几个坑
坑 1:永远不要把 Swagger/Scalar 暴露到生产环境
if (app.Environment.IsDevelopment()) 这行守护必不可少。生产环境暴露 OpenAPI 文档等于把你的 API 攻击面完整地告诉攻击者——所有路径、所有参数、所有返回结构。
如果业务确实需要生产环境也能访问 API 文档(比如开发者门户),「必须给文档本身也加上鉴权」:
坑 2:HTTPS 是 OAuth 的硬性要求
OAuth 2.0 在 HTTP 上无法工作(除了 localhost 特殊放行)。开发环境用 dotnet run 启动时确保走 HTTPS 端点,否则授权回调会失败。
坑 3:Token 过期处理
JWT Token 通常 1 小时过期。开发时如果你长时间没动作,下次点 "Try it out" 会突然 401。这不是你的代码问题,是 Token 过期了,重新 Authorize 就行。
方案二的 OAuth 流程支持 refresh token,配置好之后可以做到无感续期,但这需要 IdP 端配合 offline_access scope。
坑 4:客户端凭据不要硬编码
文章里的 client_id 和 client_secret 是 Duende 演示用的公共值。你的实际项目里:
client_secret永远不应该出现在前端代码或公共仓库Swagger UI 的客户端配置可以通过 options.OAuthClientId()在服务端注入真正敏感的密钥配合 Azure Key Vault、AWS Secrets Manager 等密钥管理服务存储
六、Scalar UI 的额外优势
最后说一下为什么 .NET 10 越来越多人推荐 Scalar 替代 Swagger UI。
「第一,更现代的界面」。Scalar 基于 OpenAPI 3.1 设计,界面响应式、支持深色模式、支持搜索。
「第二,更友好的 Token 体验」。Scalar 的认证区域不是弹窗而是侧边栏常驻,token 输入一次后整个会话都可见。这对调试体验是质的提升。
「第三,多语言 SDK 代码示例」。点击任何端点,Scalar 自动生成 cURL、C#、Python、JavaScript、Go 等多种语言的调用代码,「而且自动带上你已经输入的 Token」。这对前后端联调极其方便。
「第四,零配置兼容现有 OpenAPI 文档」。本文方案一的 Transformer 代码一行不改,把 UseSwaggerUI 换成 MapScalarApiReference 就能切换 UI——这就是基于 OpenAPI 标准的好处。
如果你的项目刚开始做 .NET 10 升级,「直接上 Scalar,不必再折腾 Swashbuckle 了」。
最后
API 文档的鉴权配置,看起来是个小问题,但它直接影响每个开发者每天的调试体验。一个配置得当的 Swagger / Scalar UI,能让团队的 API 联调效率提升一倍以上。
总结一下本文的两条主线:
「简单方案」:用 IOpenApiDocumentTransformer 注入 Bearer Security Scheme,几十行代码搞定,适合内部项目和原型验证。
「企业方案」:用 OAuth 2.0 Authorization Code Flow + PKCE,对接 IdentityServer 之类的身份服务,一键跳转登录,适合对外平台和企业级应用。
两套方案都基于 .NET 10 官方的 Microsoft.AspNetCore.OpenApi 体系,配置同时兼容 Swagger UI 和 Scalar UI——这是新版 OpenAPI 标准化最大的福利。
下次再有人抱怨"Swagger 加了鉴权调不通",把这篇文章甩给他就行。