实现说明
配置文件(appsettings.json)
- 集中管理 Swagger 相关配置,包括允许访问的 IP 列表、文档标题和版本
- 支持环境特定配置(如 appsettings.Development.json),方便多环境部署
SwaggerSettings 类
- 强类型绑定配置文件中的 Swagger 节点
- 提供类型安全的配置访问,避免硬编码字符串键
SwaggerIPFilterMiddleware 中间件
- 自动拦截 Swagger 相关路径(/swagger 和 /swagger-ui)
- 支持获取真实客户端 IP(包括反向代理场景)
- 处理 IP 格式清洗(移除端口号,兼容 IPv4 和 IPv6)
- 结合日志记录未授权访问尝试
Program.cs 配置
- 启用配置文件热重载(reloadOnChange: true)
- 通过 IOptions 模式注入配置,自动感知配置变化
- 提供扩展方法简化中间件注册
使用方法
- 修改 appsettings.json 中的 Swagger:AllowedIPs 添加允许访问的 IP
- 无需重启应用,配置会自动生效
- 非允许 IP 访问 Swagger 时会收到 403 禁止访问响应。
1、Program.cs
using SwaggerIpFilterDemo;
var builder = WebApplication.CreateBuilder(args);
// 配置:启用配置文件热重载
builder.Configuration
.SetBasePath(builder.Environment.ContentRootPath)
.AddJsonFile("appsettings.json", reloadOnChange: true, optional: false)
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", reloadOnChange: true, optional: true)
.AddEnvironmentVariables();
// 注册Swagger配置
builder.Services.Configure<SwaggerSettings>(builder.Configuration.GetSection("Swagger"));
// 添加控制器服务
builder.Services.AddControllers();
// 添加Swagger服务
builder.Services.AddSwaggerGen(c =>
{
var swaggerSettings = builder.Configuration.GetSection("Swagger").Get<SwaggerSettings>();
c.SwaggerDoc(swaggerSettings.Version, new() { Title = swaggerSettings.Title, Version = swaggerSettings.Version });
});
var app = builder.Build();
// 开发环境启用详细异常页
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// 注册Swagger IP过滤中间件(必须在UseSwagger之前)
app.UseSwaggerIPFilter();
// 启用Swagger
app.UseSwagger();
app.UseSwaggerUI(c =>
{
var swaggerSettings = app.Services.GetRequiredService<IOptions<SwaggerSettings>>().Value;
c.SwaggerEndpoint($"/swagger/{swaggerSettings.Version}/swagger.json", swaggerSettings.Title);
});
// 其他中间件
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.Run();
2、SwaggerIPFilterMiddleware.cs
namespace SwaggerIpFilterDemo;
/// <summary>
/// Swagger IP访问过滤中间件
/// </summary>
public class SwaggerIPFilterMiddleware
{
private readonly RequestDelegate _next;
private readonly IOptions<SwaggerSettings> _swaggerSettings;
private readonly ILogger<SwaggerIPFilterMiddleware> _logger;
/// <summary>
/// 构造函数
/// </summary>
public SwaggerIPFilterMiddleware(
RequestDelegate next,
IOptions<SwaggerSettings> swaggerSettings,
ILogger<SwaggerIPFilterMiddleware> logger)
{
_next = next;
_swaggerSettings = swaggerSettings;
_logger = logger;
}
/// <summary>
/// 中间件执行逻辑
/// </summary>
public async Task InvokeAsync(HttpContext context)
{
// 只拦截Swagger相关请求
if (context.Request.Path.StartsWithSegments("/swagger") ||
context.Request.Path.StartsWithSegments("/swagger-ui"))
{
// 获取客户端真实IP(支持反向代理场景)
var clientIp = GetClientIpAddress(context);
// 处理IP格式(移除端口号)
var cleanIp = CleanIpAddress(clientIp);
// 检查是否为允许的IP
var allowedIps = _swaggerSettings.Value.AllowedIPs;
var isAllowed = allowedIps.Contains(cleanIp) ||
cleanIp == "127.0.0.1" ||
cleanIp == "::1";
if (!isAllowed)
{
_logger.LogWarning("Swagger访问被拒绝,IP: {Ip}", cleanIp);
context.Response.StatusCode = StatusCodes.Status403Forbidden;
await context.Response.WriteAsync($"禁止访问Swagger文档,您的IP: {cleanIp}");
return;
}
}
// 允许访问,继续处理后续中间件
await _next(context);
}
/// <summary>
/// 获取客户端真实IP(支持反向代理)
/// </summary>
private string GetClientIpAddress(HttpContext context)
{
// 优先从反向代理头获取(如Nginx/Apache设置的X-Forwarded-For)
var forwardedFor = context.Request.Headers["X-Forwarded-For"].FirstOrDefault();
if (!string.IsNullOrEmpty(forwardedFor))
{
return forwardedFor.Split(',', StringSplitOptions.TrimEntries).First();
}
// 直接从连接获取IP
return context.Connection.RemoteIpAddress?.ToString() ?? "未知IP";
}
/// <summary>
/// 清理IP地址(移除端口号)
/// </summary>
private string CleanIpAddress(string ip)
{
if (string.IsNullOrEmpty(ip)) return ip;
// 处理带端口的IP(如 "192.168.1.100:5000" → "192.168.1.100")
if (ip.Contains(':'))
{
// 区分IPv6(如"fe80::1:2:3:4:5")和带端口的IPv4
var parts = ip.Split(':');
return parts.Length > 2 ? ip : parts[0];
}
return ip;
}
}
/// <summary>
/// 中间件扩展方法,简化注册
/// </summary>
public static class SwaggerIPFilterMiddlewareExtensions
{
public static IApplicationBuilder UseSwaggerIPFilter(this IApplicationBuilder builder)
{
return builder.UseMiddleware<SwaggerIPFilterMiddleware>();
}
}
3、SwaggerSettings.cs
namespace SwaggerIpFilterDemo;
/// <summary>
/// Swagger配置选项
/// </summary>
public class SwaggerSettings
{
/// <summary>
/// 允许访问Swagger的IP列表
/// </summary>
public List<string> AllowedIPs { get; set; } = new List<string>();
/// <summary>
/// 文档标题
/// </summary>
public string Title { get; set; } = "API文档";
/// <summary>
/// 文档版本
/// </summary>
public string Version { get; set; } = "v1";
}
4、appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Swagger": {
"AllowedIPs": [
"127.0.0.1", // 本地IPv4
"::1", // 本地IPv6
"192.168.1.100",
"192.168.1.101"
],
"Title": "API文档",
"Version": "v1"
}
}