我试图在Swagger规范和SwaggerUI中包含安全头信息,因为我希望第三方系统能够轻松地使用我的API,并能够使用Swagger CodeGen和NSwag等CodeGen工具来生成客户端库。
我使用Swashbuckle / SwaggerGen在运行时自动生成文档。我的API使用了承载令牌身份验证,我希望我的SwaggerDocs反映我的身份验证方案。我还可以在我的SwaggerDocs中看到所需的安全头信息,并且能够让客户端在尝试API调用时通过SwaggerUI测试身份验证。

但是,我无法成功地将我的安全头信息显示在我自己的解决方案上。
这就是我在调试期间通过本地SwaggerDocs查看SwaggerUI时所看到的:

请注意,生成的OAS3标记在通过https://editor.swagger.io/运行时正确呈现。
下面是由Swashbuckle /SwaggerGen生成的实际标记:
{
"openapi": "3.0.1",
"info": {
"title": "MyDemo Host API v1",
"version": "v1"
},
"paths": {
"/api/services/app/Tenant/CreateTenant": {
"post": {
"tags": [
"Tenant"
],
"operationId": "ApiServicesAppTenantCreatetenantPost",
"requestBody": {
"content": {
"application/json-patch+json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/CreateTenantInput"
}
]
}
},
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/CreateTenantInput"
}
]
}
},
"text/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/CreateTenantInput"
}
]
}
},
"application/*+json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/CreateTenantInput"
}
]
}
}
}
},
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/SwaggerDocResponseWrapper"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/SwaggerDocResponseWrapper"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/SwaggerDocResponseWrapper"
}
}
}
}
},
"security": [
{
"bearer": []
}
]
}
}
},
"components": {
"schemas": {
"CreateTenantInput": {
"required": [
"adminEmailAddress",
"name",
"tenancyName"
],
"type": "object",
"properties": {
"tenancyName": {
"maxLength": 64,
"minLength": 0,
"pattern": "^[a-zA-Z][a-zA-Z0-9_-]{1,}$",
"type": "string"
},
"name": {
"maxLength": 128,
"minLength": 0,
"type": "string"
},
"adminEmailAddress": {
"maxLength": 256,
"minLength": 0,
"type": "string",
"format": "email"
},
"adminPassword": {
"maxLength": 128,
"minLength": 0,
"type": "string",
"nullable": true
},
"connectionString": {
"maxLength": 1024,
"type": "string",
"nullable": true
},
"shouldChangePasswordOnNextLogin": {
"type": "boolean"
},
"sendActivationEmail": {
"type": "boolean"
},
"editionId": {
"type": "integer",
"format": "int32",
"nullable": true
},
"isActive": {
"type": "boolean"
},
"subscriptionEndDateUtc": {
"type": "string",
"format": "date-time",
"nullable": true
},
"isInTrialPeriod": {
"type": "boolean"
},
"onSellingPartnerId": {
"type": "integer",
"format": "int32",
"nullable": true
},
"onSellingPartner": {
"allOf": [
{
"$ref": "#/components/schemas/OnSellingPartnerDto"
}
],
"nullable": true
},
"contactPersonFirstName": {
"maxLength": 32,
"type": "string",
"nullable": true
},
"contactPersonLastName": {
"maxLength": 32,
"type": "string",
"nullable": true
},
"contactNumber": {
"maxLength": 24,
"type": "string",
"nullable": true
},
"contactEmail": {
"maxLength": 256,
"type": "string",
"nullable": true
},
"taxNumber": {
"maxLength": 24,
"type": "string",
"nullable": true
},
"registeredName": {
"maxLength": 256,
"type": "string",
"nullable": true
},
"tenantBillingAddress": {
"allOf": [
{
"$ref": "#/components/schemas/TenantBillingAddressInput"
}
],
"nullable": true
}
},
"additionalProperties": false
},
"ValidationError": {
"type": "object",
"properties": {
"message": {
"type": "string",
"nullable": true
},
"members": {
"type": "array",
"items": {
"type": "string"
},
"nullable": true
}
},
"additionalProperties": false
},
"SwaggerDocResponseWrapper": {
"type": "object",
"properties": {
"result": {
"type": "string",
"nullable": true
},
"targetUrl": {
"type": "string",
"nullable": true
},
"success": {
"type": "boolean"
},
"error": {
"allOf": [
{
"$ref": "#/components/schemas/ResponseError"
}
],
"nullable": true
},
"unauthorizedRequest": {
"type": "boolean"
},
"__Abp": {
"type": "boolean"
}
},
"additionalProperties": false
},
"OnSellingPartnerDto": {
"type": "object",
"properties": {
"name": {
"type": "string",
"nullable": true
},
"isActive": {
"type": "boolean"
},
"registeredName": {
"type": "string",
"nullable": true
},
"taxNumber": {
"type": "string",
"nullable": true
},
"contactNumber": {
"type": "string",
"nullable": true
},
"contactPersonFirstName": {
"type": "string",
"nullable": true
},
"contactPersonLastName": {
"type": "string",
"nullable": true
},
"contactEmail": {
"type": "string",
"nullable": true
},
"id": {
"type": "integer",
"format": "int32"
}
},
"additionalProperties": false
},
"TenantBillingAddressInput": {
"type": "object",
"properties": {
"streetAddress": {
"maxLength": 256,
"minLength": 0,
"type": "string",
"nullable": true
},
"region": {
"maxLength": 64,
"minLength": 0,
"type": "string",
"nullable": true
},
"city": {
"maxLength": 64,
"minLength": 0,
"type": "string",
"nullable": true
},
"countryId": {
"type": "integer",
"format": "int32",
"nullable": true
},
"regionCode": {
"maxLength": 6,
"minLength": 0,
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"ResponseError": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string",
"nullable": true
},
"details": {
"type": "string",
"nullable": true
},
"validationErrors": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
},
"nullable": true
}
},
"additionalProperties": false
}
},
"securitySchemes": {
"bearer": {
"type": "http",
"description": "Specify the authorization token.",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
},
"security": [
{}
]
}环境:
(5.6.3)和Swashbuckle.AspNetCore.NewtonSoft (6.1.4)
下面的代码是从ConfiguredServices方法在Startup.cs中调用的
public override void InstallServices(IHostEnvironment hostEnvironment, IServiceCollection services, IConfiguration configuration)
{
if (WebConsts.SwaggerUiEnabled)
{
//Swagger - Enable this line and the related lines in Configure method to enable swagger UI
services.AddSwaggerGen(options =>
{
options.SwaggerDoc(ApiNames.HostApiv1, new OpenApiInfo { Title = ApiTitles.HostApiv1, Version = "v1" });
options.SwaggerDoc(ApiNames.PartnerApiv1, new OpenApiInfo { Title = ApiTitles.PartnerApiv1, Version = "v1" });
options.SwaggerDoc(ApiNames.TenantApiv1, new OpenApiInfo { Title = ApiTitles.TenantApiv1, Version = "v1" });
OpenApiSecurityScheme securityDefinition = new OpenApiSecurityScheme()
{
Name = "Bearer",
BearerFormat = "JWT",
Scheme = "bearer",
Description = "Specify the authorization token.",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
};
OpenApiSecurityRequirement securityRequirements = new OpenApiSecurityRequirement()
{
{securityDefinition, new string[] { }},
};
options.AddSecurityDefinition("bearer", securityDefinition);
// Make sure swagger UI requires a Bearer token to be specified
options.AddSecurityRequirement(securityRequirements);
options.DocInclusionPredicate((docName, apiDesc) =>
{
if (!apiDesc.ActionDescriptor.IsControllerAction())
{
return false;
}
apiDesc.TryGetMethodInfo(out MethodInfo methodInfo);
var actionDocs = methodInfo.GetCustomAttributes<SwaggerDocAttribute>()
.SelectMany(a => a.IncludeInDocuments);
var controllerDocs = methodInfo.DeclaringType.GetCustomAttributes<SwaggerDocAttribute>()
.SelectMany(a => a.IncludeInDocuments);
switch (docName)
{
case ApiNames.HostApiv1:
return apiDesc.GroupName == null ||
actionDocs.Contains(ApiNames.HostApiv1) ||
controllerDocs.Contains(ApiNames.HostApiv1);
case ApiNames.PartnerApiv1:
return apiDesc.GroupName == null ||
actionDocs.Contains(ApiNames.PartnerApiv1) ||
controllerDocs.Contains(ApiNames.PartnerApiv1);
case ApiNames.TenantApiv1:
return apiDesc.GroupName == null ||
actionDocs.Contains(ApiNames.TenantApiv1) ||
controllerDocs.Contains(ApiNames.TenantApiv1);
default:
return true;
}
});
options.IgnoreObsoleteActions();
options.IgnoreObsoleteProperties();
options.OrderActionsBy((apiDesc) => $"{apiDesc.RelativePath}");
options.ParameterFilter<SwaggerEnumParameterFilter>();
options.SchemaFilter<SwaggerEnumSchemaFilter>();
options.OperationFilter<SwaggerOperationIdFilter>();
options.OperationFilter<SwaggerOperationFilter>();
options.CustomDefaultSchemaIdSelector();
options.UseAllOfToExtendReferenceSchemas();
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath);
}).AddSwaggerGenNewtonsoftSupport();
}
}更多背景信息:我正在为API的不同类型的使用者(内部/主机应用程序;合作伙伴应用程序;普通租户/客户端应用程序)将我的API文档生成单独的SwaggerDocs。TI用SwaggerDocsAttribute装饰我的SwaggerDocsAttribute(在运行时被ABP/AspNetZero动态地用作类似REST的服务),它用来描述一个或多个Swagger文档应该将它包含在文档中的内容。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class SwaggerDocAttribute: Attribute
{
public SwaggerDocAttribute(params string[] includeInDocuments)
{
IncludeInDocuments = includeInDocuments;
}
public string[] IncludeInDocuments { get; }
}示例用法:
[SwaggerDoc(ApiNames.HostApiv1, ApiNames.PartnerApiv1, ApiNames.TenantApiv1)]
public class SystemStatusAppService: MyDemoAppServiceBase, ISystemStatusAppService
{
[ProducesResponseType(200, Type = typeof(SwaggerDocResponseWrapper))]
public async Task Ping()
{
//Do nothing - will return status code 200
}
[AbpAuthorize()]
[ProducesResponseType(200, Type = typeof(SwaggerDocResponseWrapper))]
public async Task PingWithAuth()
{
//Do nothing - will return status code 200
}
}发布于 2021-05-12 10:10:56
您可能需要在需求中添加对定义的引用。
OpenApiSecurityScheme securityDefinition = new OpenApiSecurityScheme()
{
BearerFormat = "JWT",
Scheme = "bearer",
Description = "Specify the authorization token.",
Type = SecuritySchemeType.Http,
};
options.AddSecurityDefinition("Bearer", securityDefinition);
OpenApiSecurityRequirement securityRequirements = new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
},
new string[] {}
}
};
options.AddSecurityRequirement(securityRequirements);https://stackoverflow.com/questions/67489946
复制相似问题