Asp.NET Core 2.2:Swagger 端点特定的安全定义 [英] Asp.NET Core 2.2: Swagger endpoint specific security definition

查看:73
本文介绍了Asp.NET Core 2.2:Swagger 端点特定的安全定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我的 .Net Core 2.2 REST 项目之一中使用 Swashbuckle.AspNetCore 5.0.0-rc2.在我的项目中,我提供两个不同的 api,它们在逻辑上相互连接.

I am using Swashbuckle.AspNetCore 5.0.0-rc2 in one of my .Net Core 2.2 REST projects. Within my project I am serving two different apis which are logically connected to each other.

今天,我设法将我的 swagger 文档分开,使每个 api 有一个 swagger 端点,仅包含相应的 api 控制器.

Today, I managed to seperate my swagger documentations to have one swagger endpoint per api containing only the corresponding api controllers.

我设法通过将指定的组名添加到控制器的 api 资源管理器设置来做到这一点:

I managed to do this by adding a specified group name to the api explorer settings of the controllers:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiExplorerSettings(GroupName = "contracts")]
public class ContractsController : BaseController

[Authorize(AuthenticationSchemes = "BasicAuthentication")]
[ApiExplorerSettings(GroupName = "clearing")]
public class ClearingController : BaseController

通过这些设置,我能够在 Startup.cs 中为 swagger 指定不同的端点

With that settings I was able to specify to different endpoints for swagger within my Startup.cs

  // Enable documentation middleware
  app.UseSwagger(so =>
  {
    so.RouteTemplate = "api/doc/{documentName}/swagger.json";
  });
  app.UseSwaggerUI(suo =>
  {
    suo.SwaggerEndpoint("/api/doc/contracts/swagger.json", "Contracts API");
    suo.SwaggerEndpoint("/api/doc/clearing/swagger.json", "Clearing API");
    suo.RoutePrefix = "api/doc";
    suo.SupportedSubmitMethods(SubmitMethod.Get, SubmitMethod.Post, SubmitMethod.Patch, SubmitMethod.Delete);
  });

效果很好,一切都很好.

That worked and everything was fine.

现在您可能已经注意到,我对每个 api 的控制器使用了不同的授权方法.第一个,合约 api,使用 JWT 令牌授权,而第二个,清算 api 使用基本授权.

Now as you probably noticed, I am using different authorization methods for the controllers of each api. The first, the contracts api, is using a JWT Token authorization, while the second one, the clearing api is using a Basic authorization.

我以为,swagger ui 会通过授权"属性自动使用正确的授权方法,但我错了.

I thought, the swagger ui would automatically use the correct Authorization method by the "Authorize" Attribute, but I was wrong.

好吧,我将两种授权方法都添加到 swagger ui 中间件中,如下所示:

Well I added both authorization methods to the swagger ui middleware like this:

  options.AddSecurityDefinition("Bearer", GetSwaggerTokenSecurityScheme());
  options.AddSecurityDefinition("Basic", GetSwaggerBasicSecurityScheme());

  options.AddSecurityRequirement(GetSwaggerJwtSecurityRequirement());
  options.AddSecurityRequirement(GetSwaggerBasicSecurityRequirement());

这是我完整的招摇配置代码:

Heres my full swagger configuration code:

/// <summary>
/// Configures the swagger generation
/// </summary>
/// <param name="config">The swagger configuration</param>
/// <param name="options">The swagger gen options instance</param>
public static void ConfigureSwaggerGen(IConfiguration config, SwaggerGenOptions options)
{
  var swaggerConfig = config.Get<SwaggerConfiguration>();
  AddSwaggerDocPerApiType(swaggerConfig, options);

  options.AddSecurityDefinition("Bearer", GetSwaggerTokenSecurityScheme());
  options.AddSecurityDefinition("Basic", GetSwaggerBasicSecurityScheme());

  options.AddSecurityRequirement(GetSwaggerJwtSecurityRequirement());
  options.AddSecurityRequirement(GetSwaggerBasicSecurityRequirement());

  if (!swaggerConfig.SwaggerIncludeXml)
  {
    return;
  }
  var xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.xml");
  xmlFiles.ToList().ForEach(f => options.IncludeXmlComments(f));
  options.DescribeAllEnumsAsStrings();
}

/// <summary>
/// Adds a swagger documentation for each api type
/// </summary>
/// <param name="config">The swagger configuration</param>
/// <param name="options">The swagger gen options instance</param>
private static void AddSwaggerDocPerApiType(SwaggerConfiguration config, SwaggerGenOptions options)
{
  options.SwaggerDoc("contracts", GetSwaggerInformationParams(config, "Contracts"));
  options.SwaggerDoc("clearing", GetSwaggerInformationParams(config, "Clearing"));
}

/// <summary>
/// Generates swagger information params object
/// according to the given configuration
/// </summary>
/// <param name="config">The configuration</param>
/// <param name="apiType">The api type</param>
/// <returns>The swagger information</returns>
private static OpenApiInfo GetSwaggerInformationParams(SwaggerConfiguration config, string apiType = "")
{
  var title = string.IsNullOrEmpty(apiType) ? config.SwaggerTitle : apiType;
  var version = string.IsNullOrEmpty(apiType) ? Assembly.GetExecutingAssembly().GetName().Version.ToString() : apiType;

  var swaggerInfo = new OpenApiInfo()
  {
    Title = title,
    Version = version.ToLower(),
    Description = config.SwaggerDescription,
    Contact = new OpenApiContact()
    {
      Name = config.SwaggerCompany,
      Email = config.SwaggerContactMail,
      Url = new Uri(config.SwaggerContactUrl)
    }
  };
  return swaggerInfo;
}

/// <summary>
/// Generates the swagger jwt security scheme object
/// </summary>
/// <returns>The swagger jwt security scheme</returns>
private static OpenApiSecurityScheme GetSwaggerTokenSecurityScheme()
{
  var scheme = new OpenApiSecurityScheme
  {
    Description = "JWT authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
    Name = "JwtAuthorization",
    In = ParameterLocation.Header,
    Type = SecuritySchemeType.ApiKey
  };
  return scheme;
}

/// <summary>
/// Generates the swagger basic security scheme object
/// </summary>
/// <returns>The swagger basic security scheme</returns>
private static OpenApiSecurityScheme GetSwaggerBasicSecurityScheme()
{
  var scheme = new OpenApiSecurityScheme
  {
    Description = "Basic authorization header. Example: \"Authorization: username:password\"",
    Name = "BasicAuthorization",
    In = ParameterLocation.Header,
    Type = SecuritySchemeType.Http,
    Scheme = "basic"
  };
  return scheme;
}

/// <summary>
/// Generates the swagger security scheme object
/// </summary>
/// <returns>The swagger security scheme</returns>
private static OpenApiSecurityRequirement GetSwaggerJwtSecurityRequirement()
{
  var req = new OpenApiSecurityRequirement()
  {
    {
      new OpenApiSecurityScheme()
      {
        Reference = new OpenApiReference() {Type = ReferenceType.SecurityScheme, Id = "Bearer"}
      },
      new[] {"readAccess", "writeAccess"}
    }
  };
  return req;
}

/// <summary>
/// Generates the swagger security scheme object
/// </summary>
/// <returns>The swagger security scheme</returns>
private static OpenApiSecurityRequirement GetSwaggerBasicSecurityRequirement()
{
  var req = new OpenApiSecurityRequirement()
  {
    {
      new OpenApiSecurityScheme()
      {
        Reference = new OpenApiReference() {Type = ReferenceType.SecurityScheme, Id = "Basic"}
      },
      new[] {"readAccess", "writeAccess"}
    }
  };
  return req;
}

现在我要实现的是,合约api控制器只有JWT令牌授权,清算api控制器只有基本授权.

Now what I want to achieve is, that only the JWT token authorization is available for the contracts api controllers and only the basic authorization is available for the clearing api controllers.

目前我总是有两种授权方法可用于任何 api:

At the moment I always have both authorization methods available for any api:

有人知道如何仅为特定文档端点指定安全性吗?

Does anybody know how to specify the security for the specific documentation endpoint only?

最好的问候

推荐答案

SwaggerGenOptions.AddSecurityRequirement 将全局应用Security Requirement,使安全图标(锁图标) 并且身份验证输入将应用于所有 API.

The SwaggerGenOptions.AddSecurityRequirement will apply the Security Requirement globally, so that the security icon (lock icon) and authentication inputs will be applied to all APIs.

以下是我仅对受保护的 API 应用安全要求的可行解决方案.

Here are the workable solution for me to ONLY apply Security Requirement on protected APIs.

  • 从全局设置中删除 SwaggerGenOptions.AddSecurityRequirement.
  • 创建一个自定义 OperationFilter,实现 Swashbuckle.AspNetCore.SwaggerGen.IOOperationFilter,并且只在受保护的 API 上添加SecurityRequirement.
  • Remove SwaggerGenOptions.AddSecurityRequirement from global settings.
  • Create a custom OperationFilter, that implements Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter, and only add SecurityRequirement on protected APIs.
public class AuthorizationOperationFilter : IOperationFilter
{
   public void Apply(OpenApiOperation operation, OperationFilterContext context)
   {
       // Get Authorize attribute
       var attributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
                               .Union(context.MethodInfo.GetCustomAttributes(true))
                               .OfType<AuthorizeAttribute>();

       if (attributes != null && attributes.Count() > 0)
       {
          var attr = attributes.ToList()[0];

          // Add what should be show inside the security section
          IList<string> securityInfos = new List<string>();
          securityInfos.Add($"{nameof(AuthorizeAttribute.Policy)}:{attr.Policy}");
          securityInfos.Add($"{nameof(AuthorizeAttribute.Roles)}:{attr.Roles}");
          securityInfos.Add($"{nameof(AuthorizeAttribute.AuthenticationSchemes)}:{attr.AuthenticationSchemes}");

          switch (attr.AuthenticationSchemes)
          {
               case var p when p == AuthenticationScheme.Basic:
                   operation.Security = new List<OpenApiSecurityRequirement>()
                   {
                        new OpenApiSecurityRequirement()
                        {
                            {
                                new OpenApiSecurityScheme
                                {
                                    Reference = new OpenApiReference
                                    {
                                        Id = "basic", // Must fit the defined Id of SecurityDefinition in global configuration
                                        Type = ReferenceType.SecurityScheme,
                                    }
                                },
                                securityInfos
                            }
                        }
                    };
                    break;

                case var p when p == AuthenticationScheme.Bearer: // = JwtBearerDefaults.AuthenticationScheme
                default:
                    operation.Security = new List<OpenApiSecurityRequirement>()
                    {
                        new OpenApiSecurityRequirement()
                        {
                            {
                                new OpenApiSecurityScheme
                                {
                                    Reference = new OpenApiReference
                                    {
                                        Id = "bearer", // Must fit the defined Id of SecurityDefinition in global configuration
                                        Type = ReferenceType.SecurityScheme
                                    }
                                },
                                securityInfos
                            }
                        }
                    };
                    break;
            }
        }
        else
        {
            operation.Security.Clear();
        }
    }
}

然后在配置SwaggerGenOptions时启用自定义OperationFilter:

Then enable the custom OperationFilter when configuring SwaggerGenOptions:

services.AddSwaggerGen(c =>
{
    
     // Set the custom operation filter
     c.OperationFilter<AuthorizationOperationFilter>();
     
     // Add JWT Authentication
     var securityScheme = new OpenApiSecurityScheme
     {
         Name = "JWT Authentication",
         Description = "Enter JWT Bearer token **_only_**",
         In = ParameterLocation.Header,
         Type = SecuritySchemeType.Http,
         Scheme = "bearer",
         BearerFormat = "JWT",
         Reference = new OpenApiReference
         {
             Id = "bearer",
             Type = ReferenceType.SecurityScheme
         }
     };
     c.AddSecurityDefinition(securityScheme.Reference.Id, securityScheme);

    
     // Add Basic Authentication
     var basicSecurityScheme = new OpenApiSecurityScheme
     {
         Name = "Basic Authentication",
         Type = SecuritySchemeType.Http,
         Scheme = "basic",
         Reference = new OpenApiReference 
         { 
              Id = "basic", 
              Type = ReferenceType.SecurityScheme 
         }
     };
     c.AddSecurityDefinition(basicSecurityScheme.Reference.Id, basicSecurityScheme);
});

请参阅我的文章示例代码更多详情.

Please refer to my article and sample code for more details.

这篇关于Asp.NET Core 2.2:Swagger 端点特定的安全定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆