You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在ASP.NET Core 6中通过API密钥授权隐藏Swagger控制器与操作

在ASP.NET Core中实现Swagger动态权限过滤的方案

完全可以实现旧项目的功能,不需要放弃该特性,核心思路是让Swagger文档每次请求都动态生成,而非启动时预生成,结合请求中的API密钥过滤接口。以下是具体实现步骤:

1. 基础配置:启用HttpContext访问

首先注册IHttpContextAccessor,让过滤器能获取当前请求上下文:

builder.Services.AddHttpContextAccessor();

2. 配置Swagger禁用预生成缓存

修改Swagger注册逻辑,禁用预生成文档,确保每次请求Swagger JSON时都重新生成:

builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "你的API名称", Version = "v1" });
    
    // 禁用启动时预生成文档,改为按需生成
    c.GenerateDocsOnBuild = false;
    
    // 向过滤器注入HttpContextAccessor,用于获取当前请求的API密钥
    var httpContextAccessor = builder.Services.BuildServiceProvider().GetRequiredService<IHttpContextAccessor>();
    c.DocumentFilter<AuthDocumentFilter>(httpContextAccessor);
    c.OperationFilter<AuthOperationFilter>(httpContextAccessor);
});

3. 实现动态过滤的DocumentFilter

自定义IDocumentFilter,根据请求中的API密钥验证权限,移除无权限的接口路径:

public class AuthDocumentFilter : IDocumentFilter
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AuthDocumentFilter(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        var httpContext = _httpContextAccessor.HttpContext;
        if (httpContext == null) return;

        // 从请求Header/查询参数中获取API密钥(根据你的实际传递方式调整)
        var apiKey = httpContext.Request.Headers["X-API-Key"].FirstOrDefault() 
                     ?? httpContext.Request.Query["apiKey"].FirstOrDefault();

        // 验证API密钥,替换为你的实际权限校验逻辑
        var hasAuthorizedAccess = ValidateApiKey(apiKey);

        // 无权限时,移除所有带[Authorize]特性的接口
        if (!hasAuthorizedAccess)
        {
            var restrictedPaths = swaggerDoc.Paths
                .Where(p => context.ApiDescriptions
                    .FirstOrDefault(a => a.RelativePath == p.Key)?
                    .ActionDescriptor.EndpointMetadata.Any(m => m is AuthorizeAttribute) == true)
                .Select(p => p.Key)
                .ToList();

            foreach (var path in restrictedPaths)
            {
                swaggerDoc.Paths.Remove(path);
            }
        }
    }

    private bool ValidateApiKey(string apiKey)
    {
        // 示例逻辑,替换为你的密钥校验规则
        return !string.IsNullOrEmpty(apiKey) && apiKey == "你的有效API密钥";
    }
}

4. 可选:实现OperationFilter细化操作过滤

如果需要更精细的操作级过滤,可自定义IOperationFilter隐藏无权限的单个接口操作:

public class AuthOperationFilter : IOperationFilter
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AuthOperationFilter(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var httpContext = _httpContextAccessor.HttpContext;
        if (httpContext == null) return;

        var apiKey = httpContext.Request.Headers["X-API-Key"].FirstOrDefault() 
                     ?? httpContext.Request.Query["apiKey"].FirstOrDefault();
        var hasAccess = ValidateApiKey(apiKey);

        // 判断当前操作是否需要授权
        var requiresAuth = context.MethodInfo.DeclaringType?.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any() == true
                           || context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();

        // 无权限时标记操作隐藏
        if (requiresAuth && !hasAccess)
        {
            operation.Extensions.Add("x-hidden", true);
        }
    }

    private bool ValidateApiKey(string apiKey)
    {
        return !string.IsNullOrEmpty(apiKey) && apiKey == "你的有效API密钥";
    }
}

5. 修改Swagger UI授权行为,触发页面刷新

默认Swagger UI的「Authorize」按钮不会刷新页面,需自定义脚本修改其行为,让用户输入密钥后自动刷新页面以加载过滤后的文档:

步骤1:配置Swagger UI注入自定义脚本

app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "你的API V1");
    // 注入自定义授权脚本
    c.InjectJavascript("/swagger-ui/custom-auth.js");
});

步骤2:创建自定义脚本文件

在wwwroot/swagger-ui目录下创建custom-auth.js:

document.addEventListener('DOMContentLoaded', function() {
    const authorizeBtn = document.querySelector('.authorize-wrapper button');
    if (authorizeBtn) {
        authorizeBtn.addEventListener('click', function() {
            // 延迟执行,确保Swagger已保存授权信息
            setTimeout(() => window.location.reload(), 500);
        });
    }
});

注意事项

  • 动态生成文档会带来一定性能开销,生产环境建议根据API密钥添加文档缓存逻辑,减少重复生成。
  • API密钥建议通过Header传递,避免暴露在URL查询参数中。
  • 若使用Swashbuckle.AspNetCore新版本,需注意API变动,及时调整过滤器实现。

内容的提问来源于stack exchange,提问作者Formentz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 13:40:34