.NET 8 引入了一项新特性:调用 MapSwagger().RequireAuthorization
来保护 Swagger UI 终结点。这是一个很有用的功能,可以防止未经授权的用户访问 API 文档。
但是,这也带来了一个问题:如何在浏览器中访问受保护的 Swagger UI 终结点?
官方示例
官方文档给出了保护 swagger 终结点的示例代码:
using System.Security.Claims;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddAuthorization();
builder.Services.AddAuthentication("Bearer").AddJwtBearer();
var app = builder.Build();
//if (app.Environment.IsDevelopment())
//{
app.UseSwagger();
app.UseSwaggerUI();
//}
app.UseHttpsRedirection();
var summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
app.MapSwagger().RequireAuthorization();
app.MapGet("/", () => "Hello, World!");
app.MapGet("/secret", (ClaimsPrincipal user) => $"Hello {user.Identity?.Name}. My secret")
.RequireAuthorization();
app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();
app.Run();
internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
在上面的代码中,/weatherforecast
终结点不需要授权,但 Swagger
终结点需要授权。
以下 Curl 传递 JWT 令牌来访问 Swagger UI 终结点:
curl -i -H "Authorization: Bearer {token}" https://localhost:{port}/swagger/v1/swagger.json
问题
虽然使用 Curl 可以访问 swagger.json,但是这并不是一个方便的方式。
一般来说,我们更倾向于使用浏览器来查看 API 文档。但是,使用浏览器访问的效果如下:
因为浏览器请求swagger.json
时没法传递 JWT 令牌。
搜索了一下,官网没有提供解决方案,网上也没有人说碰到了这个问题。
这是一个值得关注的问题,因为它影响了开发者的体验和效率。
为了解决这个问题,我们需要找到一种方法,让浏览器请求swagger.json
时能够携带 JWT 令牌。
探索
查看swagger/index.html
的源代码,我们发现swagger.json
是通过一个 JavaScript 函数加载的。
在window.onload
函数中找到这样一段:
// Parse and add interceptor functions
var interceptors = JSON.parse('{"RequestInterceptorFunction":null,"ResponseInterceptorFunction":null}');
if (interceptors.RequestInterceptorFunction)
configObject.requestInterceptor = parseFunction(interceptors.RequestInterceptorFunction);
从RequestInterceptorFunction
这个名字可以猜测,它是用来拦截请求的。
但是现在它的值为 null, 那么怎么为RequestInterceptorFunction
赋值呢?
那只能在注册 Swagger UI 的位置了。
解决
在app.UseSwaggerUI
方法中,我们找到了RequestInterceptorFunction
属性,它的帮助文档里的示例如下:
/// Ex: "function (req) { req.headers['MyCustomHeader'] = 'CustomValue'; return req; }"
这正是我们想要的效果!
请求swagger.json
时传递 JWT 令牌。
最后实现代码如下:
var js = @"function (req) {
if(req.url.includes('swagger.json'))
{
searchParams = new URLSearchParams(window.location.search);
if (searchParams.has('token'))
{
req.headers['Authorization'] = 'Bearer '+searchParams.get('token');
}
}
return req;
}";
app.UseSwaggerUI(option=>option.Interceptors.RequestInterceptorFunction = js.Replace("\r\n",""));
当请求swagger.json
时,我们将浏览器地址栏中的token
参数添加到Authorization Header
中。
总结
.NET 8 引入了一项新特性:调用 MapSwagger().RequireAuthorization
来保护 Swagger UI 终结点。这是一个很有用的功能,可以防止未经授权的用户访问 API 文档。
但是,官方并没提供如何在浏览器中优雅地访问受保护的 Swagger UI 终结点的方案。
我们通过修改 Swagger UI 的配置,实现了在浏览器中携带 JWT 令牌访问 Swagger UI 的功能。
如果你有其他想法,欢迎留言分享!