在SwaggerUI中加入登录验证是一项早在之前就已经完成的工作。然而,之前的方法总感觉有点硬编码。近期,.Net8增加了一个新特性:调用
MapSwagger().RequireAuthorization
来保护SwaggerUI。但是官方的功能似乎还不够完整,只能使用postman、curl等工具带上
Authorization
头来请求。在浏览器中打开则直接返回401错误。
近期有一个项目需要使用这个功能,因此我将之前完成的SwaggerUI登录认证中间件拿出来进行了重构。
这次我依然使用基本身份验证的方式来登录,写了一个自定义的
SwaggerAuthenticationHandler
,通过
Microsoft.AspNetCore.Authentication
提供的扩展方法来实现登录。
这次我试着不按照写代码的顺序,而是站在使用者的角度来介绍,也许会更直观一些。
编辑
src/IdsLite.Api/Extensions/CfgSwagger.cs
文件(这是用来配置Swagger的相关扩展方法)
其他的都是常规的配置,重点在于
app.UseMiddleware<SwaggerBasicAuthMiddleware>();
添加了一个中间件。
来编写这个中间件,代码路径
src/IdsLite.Api/Middlewares/SwaggerBasicAuthMiddleware.cs
主要逻辑在
InvokeAsync
方法里。判断当前地址以
/swagger
开头的话,就进入身份认证流程,如果配置了其他SwaggerUI地址,记得同步修改这个中间件的配置,或者做成通用的配置,避免硬编码。
这里使用了
Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions
提供的扩展方法
context.AuthenticateAsync("Scheme Name")
来验证身份(具体的scheme我们后面会实现)。
如果验证失败的话,返回401,同时添加响应头
WWW-Authenticate:Basic
,这样就能在浏览器里弹出输入用户名和密码的提示框了。
在注册
Authentication
服务的时候,可以添加一些其他的scheme。
注册服务的代码大概是这样
services.AddAuthentication(options => {
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(…)
.AddScheme<AuthenticationSchemeOptions, SwaggerAuthenticationHandler>(AuthSchemes.Swagger, null);
AddScheme
方法可以添加各种类型的认证方案,这里添加了一个自定义的认证方案
SwaggerAuthenticationHandler
,后面的参数是方案的名称和选项。
为了避免硬编码,我写了个静态类
接下来实现这个自定义的认证方案。
其实就是把基本身份验证和固定用户名和密码结合在一起。不过为了不在代码里硬编码,我把用户名和密码放在配置里了,通过注入
IOption<T>
的方式获取。也可以放在数据库里,通过EFCore之类的去读取。
还可以集成OpenIDConnect和OAuth,我还没有实践,详情见参考资料。
既要在项目发布后访问SwaggerUI,又要保证一定的安全性,本文提供的思路或许是一种比较简单又有效的解决方案。