. net 5向未授权用户隐藏swagger端点



我有一个使用OpenApi的。net 5 API。

是否有可能在swagger中隐藏所有API端点,但登录端点除外,直到用户获得JWT承载令牌授权?

这是我在startup.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>()
}
});
});

在身份验证之前,我通过破解中间件来从swagger中删除端点,从而隐藏了swagger端点。使用swagger请求/响应拦截器来持久化接收到的令牌,并在用户登录后刷新页面以重新获取swagger。json文件。

我把解写在这里:https://medium.com/@milad665/隐藏端点——-大摇大摆ui - -未经身份验证的用户- 4054 a4e15b89

您需要实现自己的中间件并检查端点路径。如果以"/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();
});
}
}

然后在你的startup.cs(以下顺序问题)

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

首先创建并添加一个新的DocumentFilter,从您的swagger.json中为未经授权的用户删除所有信息。您可以非常具体地删除或保留什么,但本例只是删除所有端点和模式,但保留授权所需的Auth信息。

public class RequireAuthenticationDocumentFilter : IDocumentFilter
{
private readonly IHttpContextAccessor _httpContextAccessor;
public RequireAuthenticationDocumentFilter(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}

public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
bool isAuthenticated =
_httpContextAccessor.HttpContext?.User.Identity?.IsAuthenticated ?? false;

if (!isAuthenticated)
{
swaggerDoc.Paths.Clear();
context.SchemaRepository.Schemas.Clear();
}
}
}

添加RequireAuthenticationDocumentFilter。现在,您应该在swagger.json和SwaggerUI中看不到端点或模式。

services.AddSwaggerGen(options =>
{
options.DocumentFilter<RequireAuthenticationDocumentFilter>();
}

下一步是配置SwaggerUI以在页面重新加载之间持久化Auth令牌。然后,RequestInterceptor(一个可以注入的JavaScript函数)在请求swagger.json时使用持久化令牌。

app.UseSwaggerUI(options =>
{
options.EnablePersistAuthorization();
options.UseRequestInterceptor(
"(request) => {" +
// "  debugger;" +
"  if (!request.url.endsWith('swagger.json')) return request;" +
"  var json = window.localStorage?.authorized;" +
"  if (!json) return request;" +
"  var auth = JSON.parse(json);" +
"  var token = auth?.oauth2?.token?.access_token;" +
"  if (!token) return request;" +
"  request.headers.Authorization = 'Bearer ' + token;" +
"  return request;" +
"}");
}

注意,swagger.json是在SwaggerUI页面加载时请求的。通过SwaggerUI授权后,您需要手动重新加载页面以便再次请求swagger.json,但这次使用持久的授权信息。

在检查RequireAuthenticationDocumentFilter中的身份验证时遇到问题时,请确保在将Swagger和SwaggerUI添加到您的ASP之前进行身份验证和授权。. NET Core Middleware Pipeline.

...
app.UseAuthentication();
app.UseAuthorization();
app.UseSwagger();
app.UseSwaggerUI();
...

我最终使用appsettings隐藏了swagger端点。json参数,不完全是我所要求的,但我将发布解决方案,以防它帮助别人,因为它可以过滤登录用户:

有一些注释块和未使用的代码可能对你有用,因为它是我在网上找到的例子。

Swagger ignore filter class:

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 ConfigureServices:

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>()
}
});
});

最新更新