微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

不记名令牌身份验证不适用于 Swashbuckle 和 ASPNetZero / ABP

如何解决不记名令牌身份验证不适用于 Swashbuckle 和 ASPNetZero / ABP

我正在尝试在我的 Swagger 规范和 SwaggerUI 中包含安全标头信息,因为我希望 3rd 方系统能够轻松使用我的 API,并且能够使用诸如 Swagger CodeGen 和 NSwag 之类的 CodeGen 工具来生成客户端库来自它。

我正在使用 Swashbuckle / SwaggerGen 在运行时自动生成文档。 我的 API 使用了不记名令牌身份验证,并且我希望我的 SwaggerDocs 反映我的身份验证方案。我还可以在我的 SwaggerDocs 中看到所需的安全标头信息,并能够让客户端在尝试 API 调用时通过 SwaggerUI 测试身份验证。

enter image description here

但是,我一直无法在我自己的解决方案中成功显示我的安全标头信息。

这是我目前在调试期间通过本地 SwaggerUI 查看 SwaggerDocs 时看到的内容

enter image description here

请注意,生成的 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 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?