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

基于Microsoft Identity的Swagger接口按角色隐藏失效问题

问题原因与解决方案

你当前的代码虽然进入了!isUserInRole分支,但只是修改了OpenApiOperation的属性(清除标签、摘要等),并没有从Swagger文档的核心路径集合中移除对应的接口条目,所以Swagger UI依然会显示该接口。要彻底隐藏接口,需要直接操作Swagger文档的Paths集合,改用IDocumentFilter实现,而不是IOperationFilter。

步骤1:修改过滤器实现为IDocumentFilter

替换原HideEndpointsBasedOnUserFilter类的代码,直接操作整个Swagger文档来移除不需要的接口:

using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

public class HideEndpointsBasedOnUserFilter : IDocumentFilter
{
    private readonly IHttpContextAccessor _httpContextAccessor;

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

    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        var user = _httpContextAccessor.HttpContext?.User;

        if (user == null || !user.Identity.IsAuthenticated)
        {
            return;
        }

        // 收集需要移除的路径和操作
        var pathsToRemove = new List<KeyValuePair<string, OpenApiPathItem>>();
        foreach (var pathEntry in swaggerDoc.Paths)
        {
            var operationsToRemove = new List<OperationType>();
            foreach (var operationEntry in pathEntry.Value.Operations)
            {
                // 匹配当前操作对应的控制器Action
                var apiDesc = context.ApiDescriptions.FirstOrDefault(api =>
                    api.RelativePath == pathEntry.Key.Replace("/cabinetGatewaySvc/", "") &&
                    api.HttpMethod.Equals(operationEntry.Key.ToString(), System.StringComparison.OrdinalIgnoreCase));
                
                if (apiDesc?.ActionDescriptor is not ControllerActionDescriptor actionDescriptor)
                    continue;

                // 检查是否存在SwaggerHideAttribute
                var hideAttribute = actionDescriptor.MethodInfo.GetCustomAttribute<SwaggerHideAttribute>();
                if (hideAttribute == null)
                    continue;

                var requiredRole = hideAttribute.RequiredRole;
                var isUserInRole = user.IsInRole(requiredRole);

                Console.WriteLine($"Required role: {requiredRole}, Is user in role: {isUserInRole}");

                if (!isUserInRole)
                {
                    operationsToRemove.Add(operationEntry.Key);
                }
            }

            // 移除当前路径下的目标操作
            foreach (var opType in operationsToRemove)
            {
                pathEntry.Value.Operations.Remove(opType);
            }

            // 如果路径下没有剩余操作,移除整个路径
            if (!pathEntry.Value.Operations.Any())
            {
                pathsToRemove.Add(pathEntry);
            }
        }

        // 移除空路径
        foreach (var pathEntry in pathsToRemove)
        {
            swaggerDoc.Paths.Remove(pathEntry.Key);
        }
    }
}

步骤2:更新Swagger注册配置

在Program.cs的AddSwaggerGen中,将原来的OperationFilter替换为DocumentFilter:

builder.Services.AddSwaggerGen(c => {
    c.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "Cabinet gateway",
        Version = "v1",
        Description = "Cabinet gateway documentation."
    });

    // 替换为DocumentFilter
    c.DocumentFilter<HideEndpointsBasedOnUserFilter>();

    // ... 其他原有配置(XML注释、安全定义等)保持不变 ...
});

步骤3:禁用Swagger文档缓存

Swagger默认会缓存生成的文档,导致用户登录后看不到实时的接口权限变化,需要在UseSwagger中添加缓存控制:

app.UseSwagger(c =>
{
    c.RouteTemplate = "cabinetGatewaySvc/swagger/{documentname}/swagger.json";
    // 添加缓存控制头,禁止浏览器缓存Swagger文档
    c.PreSerializeFilters.Add((swaggerDoc, httpReq) =>
    {
        httpReq.HttpContext.Response.Headers.Append("Cache-Control", "no-cache, no-store, must-revalidate");
        httpReq.HttpContext.Response.Headers.Append("Pragma", "no-cache");
        httpReq.HttpContext.Response.Headers.Append("Expires", "0");
    });
});

额外修复:原代码中的语法错误

原IOperationFilter实现中,获取SwaggerHideAttribute的代码有语法错误,缺少闭合的>:

// 错误写法
var hideAttribute = context.MethodInfo.GetCustomAttribute<SwaggerHideAttribute ();

// 正确写法
var hideAttribute = context.MethodInfo.GetCustomAttribute<SwaggerHideAttribute>();

完成以上修改后,没有对应角色的登录用户将不会在Swagger UI中看到标记了[SwaggerHide("Admin")]的接口。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 07:35:02