提问者:小点点

.NET-5 向未经授权的用户隐藏大摇大摆的endpoint


我有一个。NET 5 API使用OpenApi。

是否可以隐藏所有APIendpoint,但登录endpoint,直到用户获得JWT不记名令牌授权?

这是我在启动时使用的代码.cs

services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo { 
                Title = "API", Version = "v1",
                Description = "API (.NET 5.0)",
                Contact = new OpenApiContact()
                {
                    Name = "Contact",
                    Url = null,
                    Email = "email@email.com"
                }
            });
            c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
            {
                Description = @"Autorización JWT utilizando el esquema Bearer en header. <br />
                  Introducir el token JWT generado por AuthApi.",
                Name = "Authorization",
                In = ParameterLocation.Header,
                Type = SecuritySchemeType.Http,
                Scheme = "Bearer"
            });
            c.AddSecurityRequirement(new OpenApiSecurityRequirement()
  {
    {
      new OpenApiSecurityScheme
      {
        Reference = new OpenApiReference
          {
            Type = ReferenceType.SecurityScheme,
            Id = "Bearer"
          },
          Scheme = "oauth2",
          Name = "Bearer",
          In = ParameterLocation.Header,

        },
        new List<string>()
      }
    });
        });

共3个答案

匿名用户

我设法在身份验证之前隐藏了swaggerendpoint,方法是侵入一个中间件,为未经身份验证的用户从swagger.json文件中删除endpoint,并使用swagger请求/响应拦截器来持久存储收到的令牌,并在用户登录后刷新页面以重新获取swagger.json文件。

我在这里写下了解决方案: https://medium.com/@milad665/hide-endpoint in-swagger-用户界面-for-unAuthated-users-4054a4e15b89

匿名用户

您需要实现自己的中间件并检查endpoint路径。如果它以“/swagger”开头,那么您应该挑战身份验证。

下面是其他人编写的代码

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using System;

/// <summary>
/// The extension methods that extends <see cref="IApplicationBuilder" /> for authentication purposes
/// </summary>
public static class ApplicationBuilderExtensions
{
    /// <summary>
    /// Requires authentication for paths that starts with <paramref name="pathPrefix" />
    /// </summary>
    /// <param name="app">The application builder</param>
    /// <param name="pathPrefix">The path prefix</param>
    /// <returns>The application builder</returns>
    public static IApplicationBuilder RequireAuthenticationOn(this IApplicationBuilder app, string pathPrefix)
    {
        return app.Use((context, next) =>
        {
            // First check if the current path is the swagger path
            if (context.Request.Path.HasValue && context.Request.Path.Value.StartsWith(pathPrefix, StringComparison.InvariantCultureIgnoreCase))
            {
                // Secondly check if the current user is authenticated
                if (!context.User.Identity.IsAuthenticated)
                {
                    return context.ChallengeAsync();
                }
            }

            return next();
        });
    }
}

然后在你的启动. cs(以下顺序事项)

app.RequireAuthenticationOn("/swagger");
app.UseSwagger();
app.UseSwaggerUI();

匿名用户

我最终使用 appsettings.json 参数隐藏了招摇的点,这并不完全是我要求的,但我会发布解决方案,以防它帮助某人,因为它可以过滤登录的用户:

有一些注释的块和未使用的代码可能对您有用,正如我在网上找到的示例所提供的。

Swagger忽略过滤器类:

public class SwaggerIgnoreFilter : IDocumentFilter
{
    private IServiceProvider _provider;

    public SwaggerIgnoreFilter(IServiceProvider provider)
    {
        if (provider == null) throw new ArgumentNullException(nameof(provider));

        this._provider = provider;
    }
    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        var allTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(i => i.GetTypes()).ToList();

        var http = this._provider.GetRequiredService<IHttpContextAccessor>();
        var authorizedIds = new[] { "00000000-1111-2222-1111-000000000000" };   // All the authorized user id's.
                                                                                // When using this in a real application, you should store these safely using appsettings or some other method.
        var userId = http.HttpContext.User.Claims.Where(x => x.Type == "jti").Select(x => x.Value).FirstOrDefault();
        var show = http.HttpContext.User.Identity.IsAuthenticated && authorizedIds.Contains(userId);
        //var Securitytoken = new JwtSecurityTokenHandler().CreateToken(tokenDescriptor);
        //var tokenstring = new JwtSecurityTokenHandler().WriteToken(Securitytoken);
        //var token = new JwtSecurityTokenHandler().ReadJwtToken(tokenstring);
        //var claim = token.Claims.First(c => c.Type == "email").Value;
        Parametros parametros = new Parametros();
        if (!show)
        {
            var descriptions = context.ApiDescriptions.ToList();

            foreach (var description in descriptions)
            {
                // Expose login so users can login through Swagger. 
                if (description.HttpMethod == "POST" && description.RelativePath == "denarioapi/v1/auth/login")
                    continue;

                var route = "/" + description.RelativePath.TrimEnd('/');
                OpenApiPathItem path;
                swaggerDoc.Paths.TryGetValue(route, out path);

                switch(route)
                {
                    case string s when s.Contains("/Contabilidad"):
                        if (parametros.contabilidadApi != "1")
                        {
                            swaggerDoc.Paths.Remove(route);
                        }
                        break;
                    case string s when s.Contains("/Identificativos"):
                        if (parametros.identificativosApi != "1")
                        {
                            swaggerDoc.Paths.Remove(route);
                        }
                        break;
                    case string s when s.Contains("/Centros"):
                        if (parametros.centrosApi != "1")
                        {
                            swaggerDoc.Paths.Remove(route);
                        }
                        break;
                    case string s when s.Contains("/Contratos"):
                        if (parametros.contratosApi != "1")
                        {
                            swaggerDoc.Paths.Remove(route);
                        }
                        break;
                    
                    case string s when s.Contains("/Planificacion"):
                        if (parametros.planificacionApi != "1")
                        {
                            swaggerDoc.Paths.Remove(route);
                        }
                        break;
                    case string s when s.Contains("/Puestotrabajo"):
                        if (parametros.puestotrabajoApi != "1")
                        {
                            swaggerDoc.Paths.Remove(route);
                        }
                        break;
                    
                    case string s when s.Contains("/Usuarios"):
                        if (parametros.usuariosApi != "1")
                        {
                            swaggerDoc.Paths.Remove(route);
                        }
                        break;
                    
                    default:
                        break;
                }

                // remove method or entire path (if there are no more methods in this path)
                //switch (description.HttpMethod)
                //{
                    //case "DELETE": path. = null; break;
                    //case "GET": path.Get = null; break;
                    //case "HEAD": path.Head = null; break;
                    //case "OPTIONS": path.Options = null; break;
                    //case "PATCH": path.Patch = null; break;
                    //case "POST": path.Post = null; break;
                    //case "PUT": path.Put = null; break;
                    //default: throw new ArgumentOutOfRangeException("Method name not mapped to operation");
                //}

                //if (path.Delete == null && path.Get == null &&
                //    path.Head == null && path.Options == null &&
                //    path.Patch == null && path.Post == null && path.Put == null)
                //swaggerDoc.Paths.Remove(route);
            }

        }




        foreach (var definition in swaggerDoc.Components.Schemas)
        {
            var type = allTypes.FirstOrDefault(x => x.Name == definition.Key);
            if (type != null)
            {
                var properties = type.GetProperties();
                foreach (var prop in properties.ToList())
                {
                    var ignoreAttribute = prop.GetCustomAttribute(typeof(OpenApiIgnoreAttribute), false);

                    if (ignoreAttribute != null)
                    {
                        definition.Value.Properties.Remove(prop.Name);
                    }
                }
            }
        }
    }
}

Startup. cs配置服务:

services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo
            {
                Title = "API",
                Version = "v1",
                Description = "API (.NET 5.0)",
                Contact = new OpenApiContact()
                {
                    Name = "Contact name",
                    Url = null,
                    Email = "email@email.com"
                }
            });
            c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
            {
                Description = @"Description",
                Name = "Authorization",
                In = ParameterLocation.Header,
                Type = SecuritySchemeType.Http,
                Scheme = "Bearer"
            });
            c.DocumentFilter<SwaggerIgnoreFilter>();
            c.AddSecurityRequirement(new OpenApiSecurityRequirement()
  {
        {
          new OpenApiSecurityScheme
          {
            Reference = new OpenApiReference
              {
                Type = ReferenceType.SecurityScheme,
                Id = "Bearer"
              },
              Scheme = "oauth2",
              Name = "Bearer",
              In = ParameterLocation.Header,

            },
            new List<string>()
          }
    });
        });