Я пытаюсь скрыть, какие конечные точки отображаются в Swagger в зависимости от того, какие пользователи вошли в систему. Это не работает, и я не понимаю, почему. Я использую Microsoft Identity и Swashbuckle. В классе HideEndpointsBasedOnUserFilter код попадает в “if (!isUserInRole)” и заходит в это условие, но команды внутри не удаляют конечную точку swagger. Вот мой код:
[SwaggerHide("Admin")]
[HttpGet]
[Route("cabinetGatewaySvc/accesscontrol/{clientId}/{cardId}")]
public async Task<ActionResult<string>> AccessControl(string clientId, string cardId)
{
_twoTraceLogger.LogTrace("Вызывается CabinetController");
var result = await _canOpenPort.ValidateWearerAsync(clientId, cardId);
return Ok(result);
}
namespace MsCabinetGateway.SwaggerConfig
{
public class SwaggerHideAttribute : Attribute
{
public string RequiredRole { get; set; }
public SwaggerHideAttribute(string requiredRole)
{
RequiredRole = requiredRole;
}
}
}
public class HideEndpointsBasedOnUserFilter : IOperationFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
public HideEndpointsBasedOnUserFilter(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var user = _httpContextAccessor.HttpContext?.User;
if (user == null || !user.Identity.IsAuthenticated)
{
return; // Нет аутентифицированного пользователя, пропустить фильтрацию
}
// Проверка, есть ли у действия атрибут SwaggerHideAttribute
var hideAttribute = context.MethodInfo.GetCustomAttribute<SwaggerHideAttribute>();
if (hideAttribute != null)
{
var requiredRole = hideAttribute.RequiredRole;
var isUserInRole = user.IsInRole(requiredRole);
// Запись ролей и требуемой роли в журнал
Console.WriteLine($"Требуемая роль: {requiredRole}, Пользователь в роли: {isUserInRole}");
// Если у пользователя нет необходимой роли, скрываем конечную точку
if (!isUserInRole)
{
operation.Tags.Clear(); // Скрыть операцию, удалив теги
operation.Summary = null; // Опционально очистить аннотацию
operation.Description = null; // Опционально очистить описание
operation.Responses.Clear(); // Опционально очистить ответы
}
}
}
}
public class Program
{
private static AppSettings? ApplicationSettings { get; set; }
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
var connectionString =
builder.Configuration.GetConnectionString("MsCabinetGatewayContextConnection") ??
throw
new InvalidOperationException("Строка подключения 'MsCabinetGatewayContextConnection' не найдена.");
builder.Services.AddDbContext<MsCabinetGatewayContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddHttpContextAccessor();
builder.Services.AddDefaultIdentity<Areas.Identity.Data.MsCabinetGatewayUser>
(options => options.SignIn.RequireConfirmedAccount = true).AddEntityFrameworkStores<MsCabinetGatewayContext>();
IConfiguration configuration = builder.Configuration;
ApplicationSettings = new AppSettings();
configuration.Bind(ApplicationSettings);
builder.Services.AddControllers();
// Узнайте больше о настройке Swagger/OpenAPI на
https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Cabinet gateway",
Version = "v1",
Description = "Документация по кабинету."
});
c.OperationFilter<HideEndpointsBasedOnUserFilter>();
// Установите путь к комментариям для Swagger JSON и UI.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
c.EnableAnnotations();
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description =
Adapters.RestApiAdapter.Constants.RestProperties.Description,
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme {
Reference = new OpenApiReference {
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
builder.Services.AddHttpContextAccessor();
});
builder.Services.AddCustomConfiguration(configuration);
if (ApplicationSettings.AppInsightConfig is null)
{
ApplicationSettings.AppInsightConfig = new();
ApplicationSettings.AppInsightConfig.InstrumentationKey =
configuration.GetSection("ApplicationInsights").GetSection("InstrumentationKey").Value;
ApplicationSettings.AppInsightConfig.ConnectionString =
configuration.GetSection("ApplicationInsights").GetSection("ConnectionString").Value;
}
builder.Services.AddServiceDependencies(ApplicationSettings);
//builder.WebHost.ConfigureKestrel((context, serverOptions) =>
//{
// serverOptions.Listen(IPAddress.Any, 5000);
//});
builder.Services.AddLogging(builder =>
{
// Только Application Insights зарегистрирован как поставщик журнала
builder.AddApplicationInsights(
configureTelemetryConfiguration:
(config) => config.ConnectionString =
configuration["ApplicationInsights:ConnectionString"],
configureApplicationInsightsLoggerOptions: (options) => { }
);
});
var app = builder.Build();
GrpcAdapterExtention.AddServiceConfigurations(app);
//app.Use(async (context, next) =>
//{
// if (context.Request.Path.StartsWithSegments("/swagger")
// && !context.User.Identity.IsAuthenticated)
// {
// context.Response.Redirect("/Identity/Account/Login");
// return;
// }
// await next();
//});
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.UseStaticFiles();
app.UseSwagger();
app.UseSwaggerUI();
app.UseSwagger(c =>
{
c.RouteTemplate = "cabinetGatewaySvc/swagger/{documentname}/swagger.json";
});
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/cabinetGatewaySvc/swagger/v1/swagger.json", "Cabinet Gateway Microservice");
c.RoutePrefix = "cabinetGatewaySvc/swagger";
});
app.UseRouting();
app.UseEndpoints(endpoints =>
{
_ = endpoints.MapControllers();
});
app.Run();
}
}
Ответ
Давайте разберем вашу проблему и попробуем устранить недостатки в коде для скрытия конечных точек в Swagger в зависимости от ролей пользователей.
Проблема
Ваша логика в HideEndpointsBasedOnUserFilter
выглядит правильно на первый взгляд, но вы упоминаете, что команды внутри if (!isUserInRole)
не удаляют конечные точки из Swagger. Возможно, проблема заключается в том, что вы не полностью скрываете конечную точку, а только очищаете её теги и другие метаданные. Это не обязательно приведет к тому, что конечная точка не будет отображаться в Swagger UI.
Решение
Вместо очистки информации о конечных точках, лучше полностью исключить эти конечные точки из Swagger. Для этого можно использовать context.ApiDescription
в вашем OperationFilter
. Вот как это можно сделать:
- Измените класс
HideEndpointsBasedOnUserFilter
:
public class HideEndpointsBasedOnUserFilter : IOperationFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
public HideEndpointsBasedOnUserFilter(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var user = _httpContextAccessor.HttpContext?.User;
if (user == null || !user.Identity.IsAuthenticated)
{
return; // Не аутентифицированный пользователь
}
var hideAttribute = context.MethodInfo.GetCustomAttribute<SwaggerHideAttribute>();
if (hideAttribute != null)
{
var requiredRole = hideAttribute.RequiredRole;
var isUserInRole = user.IsInRole(requiredRole);
// Логирование ролей и требуемой роли
Console.WriteLine($"Требуемая роль: {requiredRole}, Имеет ли пользователь эту роль: {isUserInRole}");
// Если у пользователя нет необходимой роли, исключаем конечную точку
if (!isUserInRole)
{
// Исходя из контекста, исключаем конечную точку из Swagger
context.ApiDescription.HttpMethod = "DELETE"; // Удаление метода
context.ApiDescription.RelativePath = ""; // Удаление функционала
}
}
}
}
- Убедитесь, что вы правильно добавили
SwaggerHideAttribute
к вашим методам:
[SwaggerHide("Admin")]
[HttpGet]
[Route("cabinetGatewaySvc/accesscontrol/{clientId}/{cardId}")]
public async Task<ActionResult<string>> AccessControl(string clientId, string cardId)
{
// Ваш код здесь
}
Проверка конфигурации Swagger
Кроме того, убедитесь, что вы правильным образом настроили Swagger в методе ConfigureServices
:
builder.Services.AddSwaggerGen(c =>
{
// Ваша настройка Swagger
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Cabinet gateway",
Version = "v1",
Description = "Cabinet gateway documentation."
});
// Добавьте OperationFilter для фильтрации конечных точек
c.OperationFilter<HideEndpointsBasedOnUserFilter>();
// Другие настройки
});
Заключение
Попробуйте внести вышеуказанные изменения в ваш класс HideEndpointsBasedOnUserFilter
и проверьте, исчезают ли конечные точки из Swagger UI для пользователей, которые не имеют необходимых прав. Если конечные точки все равно будут отображаться, убедитесь, что в вашем проекте настроены все зависимости и компоненты правильно.