如何解决不记名令牌身份验证不适用于 Swashbuckle 和 ASPNetZero / ABP
我正在尝试在我的 Swagger 规范和 SwaggerUI 中包含安全标头信息,因为我希望 3rd 方系统能够轻松使用我的 API,并且能够使用诸如 Swagger CodeGen 和 NSwag 之类的 CodeGen 工具来生成客户端库来自它。
我正在使用 Swashbuckle / SwaggerGen 在运行时自动生成文档。 我的 API 使用了不记名令牌身份验证,并且我希望我的 SwaggerDocs 反映我的身份验证方案。我还可以在我的 SwaggerDocs 中看到所需的安全标头信息,并能够让客户端在尝试 API 调用时通过 SwaggerUI 测试身份验证。
但是,我一直无法在我自己的解决方案中成功显示我的安全标头信息。
这是我目前在调试期间通过本地 SwaggerUI 查看 SwaggerDocs 时看到的内容:
请注意,生成的 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,"adminemailAddress": {
"maxLength": 256,"type": "string","format": "email"
},"adminPassword": {
"maxLength": 128,"nullable": true
},"connectionString": {
"maxLength": 1024,"shouldChangePasswordOnNextLogin": {
"type": "boolean"
},"sendActivationEmail": {
"type": "boolean"
},"editionId": {
"type": "integer","format": "int32","isActive": {
"type": "boolean"
},"subscriptionEndDateUtc": {
"type": "string","format": "date-time","isInTrialPeriod": {
"type": "boolean"
},"onSellingPartnerId": {
"type": "integer","onSellingPartner": {
"allOf": [
{
"$ref": "#/components/schemas/OnSellingPartnerDto"
}
],"contactPersonFirstName": {
"maxLength": 32,"contactPersonLastName": {
"maxLength": 32,"contactNumber": {
"maxLength": 24,"contactEmail": {
"maxLength": 256,"taxnumber": {
"maxLength": 24,"registeredname": {
"maxLength": 256,"tenantBillingAddress": {
"allOf": [
{
"$ref": "#/components/schemas/TenantBillingAddressInput"
}
],"nullable": true
}
},"additionalProperties": false
},"ValidationError": {
"type": "object","properties": {
"message": {
"type": "string","members": {
"type": "array","items": {
"type": "string"
},"SwaggerDocResponseWrapper": {
"type": "object","properties": {
"result": {
"type": "string","targetUrl": {
"type": "string","success": {
"type": "boolean"
},"error": {
"allOf": [
{
"$ref": "#/components/schemas/ResponseError"
}
],"unauthorizedRequest": {
"type": "boolean"
},"__Abp": {
"type": "boolean"
}
},"OnSellingPartnerDto": {
"type": "object","properties": {
"name": {
"type": "string","registeredname": {
"type": "string","taxnumber": {
"type": "string","contactNumber": {
"type": "string","contactPersonFirstName": {
"type": "string","contactPersonLastName": {
"type": "string","contactEmail": {
"type": "string","id": {
"type": "integer","format": "int32"
}
},"TenantBillingAddressInput": {
"type": "object","properties": {
"streetAddress": {
"maxLength": 256,"region": {
"maxLength": 64,"city": {
"maxLength": 64,"countryId": {
"type": "integer","regionCode": {
"maxLength": 6,"ResponseError": {
"type": "object","properties": {
"code": {
"type": "integer","format": "int32"
},"message": {
"type": "string","details": {
"type": "string","validationErrors": {
"type": "array","items": {
"$ref": "#/components/schemas/ValidationError"
},"additionalProperties": false
}
},"securitySchemes": {
"bearer": {
"type": "http","description": "Specify the authorization token.","scheme": "bearer","bearerFormat": "JWT"
}
}
},"security": [
{}
]
}
环境:
- AspNetZero 核心 MVC 和 JQuery v10.3.0 (.Net 5)
- Abp (6.2.0)
- Swashbuckle.AspNetCore (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 文档生成到单独的 SwaggerDocs 中,用于我的 API 的不同类型的使用者(内部/主机应用程序;合作伙伴应用程序;普通租户/客户端应用程序)。 TI 使用 SwaggerDocsAttribute
装饰我的 applicationservices(在运行时由 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
}
}
解决方法
您可能需要在需求中添加对定义的引用。
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);
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。