Asp.NET 核心 2.2:Swagger 端点特定的安全定义
Asp.NET Core 2.2: Swagger endpoint specific security definition
我在我的一个 .Net Core 2.2 REST 项目中使用 Swashbuckle.AspNetCore 5.0.0-rc2。在我的项目中,我服务于两个不同的 apis,它们在逻辑上相互连接。
今天,我设法将我的 swagger 文档分开,使每个 api 有一个 swagger 端点,仅包含相应的 api 控制器。
我通过将指定的组名添加到控制器的 api 资源管理器设置来做到这一点:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiExplorerSettings(GroupName = "contracts")]
public class ContractsController : BaseController
[Authorize(AuthenticationSchemes = "BasicAuthentication")]
[ApiExplorerSettings(GroupName = "clearing")]
public class ClearingController : BaseController
通过该设置,我能够在 Startup.cs
中指定不同的端点以进行 swagger
// 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);
});
成功了,一切都很好。
现在您可能已经注意到,我对每个 api 的控制器使用不同的授权方法。第一个合同 api 使用 JWT 令牌授权,而第二个合同 api 使用基本授权。
我以为,大摇大摆的 ui 会通过 "Authorize" 属性自动使用正确的授权方法,但我错了。
好吧,我将这两种授权方法都添加到了 swagger ui 中间件中,如下所示:
options.AddSecurityDefinition("Bearer", GetSwaggerTokenSecurityScheme());
options.AddSecurityDefinition("Basic", GetSwaggerBasicSecurityScheme());
options.AddSecurityRequirement(GetSwaggerJwtSecurityRequirement());
options.AddSecurityRequirement(GetSwaggerBasicSecurityRequirement());
这是我完整的 swagger 配置代码:
/// <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;
}
现在我想要实现的是,只有 JWT 令牌授权可用于合约 api 控制器,只有基本授权可用于清算 api 控制器。
目前我总是有两种授权方法可用于任何 api:
有人知道如何仅为特定文档端点指定安全性吗?
此致
根据此处的信息:
您需要删除语句 'options.AddSecurityRequirement'
添加的全局安全要求
并将其替换为 'options.AddSecurityDefinition' 定义的安全操作,并将其绑定到应用于操作的授权语句
swagger.io 的 link 演示了所要求的安全性的各种模式
希望对您有所帮助
+研发
SwaggerGenOptions.AddSecurityRequirement
将全局应用安全要求,以便安全图标(锁定图标)和身份验证输入将应用于所有 API。
以下是我仅对受保护的 API 应用安全要求的可行解决方案。
- 从全局设置中删除
SwaggerGenOptions.AddSecurityRequirement
。
- 创建一个实现
Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter
的自定义 OperationFilter,并且仅在受保护的 API 上添加 SecurityRequirement。
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();
}
}
}
然后在配置时启用自定义OperationFilter 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);
});
详情请参考我的article and sample code
我在我的一个 .Net Core 2.2 REST 项目中使用 Swashbuckle.AspNetCore 5.0.0-rc2。在我的项目中,我服务于两个不同的 apis,它们在逻辑上相互连接。
今天,我设法将我的 swagger 文档分开,使每个 api 有一个 swagger 端点,仅包含相应的 api 控制器。
我通过将指定的组名添加到控制器的 api 资源管理器设置来做到这一点:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiExplorerSettings(GroupName = "contracts")]
public class ContractsController : BaseController
[Authorize(AuthenticationSchemes = "BasicAuthentication")]
[ApiExplorerSettings(GroupName = "clearing")]
public class ClearingController : BaseController
通过该设置,我能够在 Startup.cs
中指定不同的端点以进行 swagger // 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);
});
成功了,一切都很好。
现在您可能已经注意到,我对每个 api 的控制器使用不同的授权方法。第一个合同 api 使用 JWT 令牌授权,而第二个合同 api 使用基本授权。
我以为,大摇大摆的 ui 会通过 "Authorize" 属性自动使用正确的授权方法,但我错了。
好吧,我将这两种授权方法都添加到了 swagger ui 中间件中,如下所示:
options.AddSecurityDefinition("Bearer", GetSwaggerTokenSecurityScheme());
options.AddSecurityDefinition("Basic", GetSwaggerBasicSecurityScheme());
options.AddSecurityRequirement(GetSwaggerJwtSecurityRequirement());
options.AddSecurityRequirement(GetSwaggerBasicSecurityRequirement());
这是我完整的 swagger 配置代码:
/// <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;
}
现在我想要实现的是,只有 JWT 令牌授权可用于合约 api 控制器,只有基本授权可用于清算 api 控制器。
目前我总是有两种授权方法可用于任何 api:
有人知道如何仅为特定文档端点指定安全性吗?
此致
根据此处的信息:
您需要删除语句 'options.AddSecurityRequirement'
添加的全局安全要求并将其替换为 'options.AddSecurityDefinition' 定义的安全操作,并将其绑定到应用于操作的授权语句
swagger.io 的 link 演示了所要求的安全性的各种模式
希望对您有所帮助
+研发
SwaggerGenOptions.AddSecurityRequirement
将全局应用安全要求,以便安全图标(锁定图标)和身份验证输入将应用于所有 API。
以下是我仅对受保护的 API 应用安全要求的可行解决方案。
- 从全局设置中删除
SwaggerGenOptions.AddSecurityRequirement
。 - 创建一个实现
Swashbuckle.AspNetCore.SwaggerGen.IOperationFilter
的自定义 OperationFilter,并且仅在受保护的 API 上添加 SecurityRequirement。
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();
}
}
}
然后在配置时启用自定义OperationFilter 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);
});
详情请参考我的article and sample code