以编程方式添加安全方案时,模式从组件中消失

Posted

技术标签:

【中文标题】以编程方式添加安全方案时,模式从组件中消失【英文标题】:Schemas disappear from components when programmatically adding security scheme 【发布时间】:2021-03-27 14:28:18 【问题描述】:

我最近从 Springfox 转换为 Springdoc-openapi,以便为我的 Spring Boot Rest API 服务生成我的 OpenAPI。

在我添加安全方案之前,一切都运行良好。一旦我这样做了,我的方案将不再出现,并且 SwaggerUI 页面上会出现错误:

Could not resolve reference: Could not resolve pointer: /components/schemas/Ping does not exist in document

我正在以编程方式设置我的配置,并且有 2 个组。

我正在使用 Spring Boot v2.4.0 和 springdoc-openapi-ui v1.5.1

我的 pom.xml 的片段:

        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-ui</artifactId>
            <version>1.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-hateoas</artifactId>
            <version>1.5.1</version>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-security</artifactId>
            <version>1.5.1</version>
        </dependency>

配置片段:

    @Bean
public GroupedOpenApi apiV1() 
    String[] paths = "/v1/**";
    String[] packagesToScan = "com.test.controller";
    return GroupedOpenApi.builder()
            .group("v1")
            .packagesToScan(packagesToScan)
            .pathsToMatch(paths)
            .addOpenApiCustomiser(buildV1OpenAPI())
            .build();


@Bean
public GroupedOpenApi apiV2() 
    String[] paths = "/v2/**";
    String[] packagesToScan = "com.test.controller";
    return GroupedOpenApi.builder()
            .group("v2")
            .packagesToScan(packagesToScan)
            .pathsToMatch(paths)
            .addOpenApiCustomiser(buildV2OpenAPI())
            .build();


public OpenApiCustomiser buildV1OpenAPI() 
    return openApi -> openApi.info(apiInfo().version("v1"));


public OpenApiCustomiser buildV2OpenAPI() 
    final String securitySchemeName = "Access Token";
    return openApi -> openApi.info(apiInfo().version("v2"))
            .addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
            .components(new Components().addSecuritySchemes(securitySchemeName, new SecurityScheme()
                    .type(SecurityScheme.Type.APIKEY)
                    .in(SecurityScheme.In.HEADER)
                    .name(HttpHeaders.AUTHORIZATION)));


// Describe the apis
private Info apiInfo() 
    return new Info()
            .title("Title")
            .description("API Description");

对于我的 v1 组,一切正常。我的 Schemas 出现在 Swagger UI 页面上,我在生成的 api-doc 的 components 部分看到它们。

    "components": 
    "schemas": 
        "ApplicationErrorResponse": 
            ...
            
        ,
        "Ping": 
            ...
        
    

对于我的 v2 组,不生成架构。

    "components": 
    "securitySchemes": 
        "Access Token": 
            "type": "apiKey",
            "name": "Authorization",
            "in": "header"
        
    

知道为什么在以编程方式将安全方案添加到 OpenAPI 组件时没有自动扫描和添加我的模式吗?我的配置中是否缺少某些内容?

这是我在控制器中的请求映射。

@Operation(summary = "Verify API and backend connectivity",
        description = "Confirm connectivity to the backend, as well and verify API service is running.")
@OkResponse
@GetMapping(value = API_VERSION_2 + "/ping", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Ping> getPingV2(HttpServletRequest request) 

...

这是我的@OkResponse 注释:

    @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE, ElementType.METHOD)
@Documented
@ApiResponse(responseCode = HTTP_200,
        description = HTTP_200_OK,
        headers = 
                @Header(name = CONTENT_VERSION_HEADER, description = CONTENT_VERSION_HEADER_DESCRIPTION, schema = @Schema(type = "string")),
                @Header(name = DEPRECATION_MESSAGE_HEADER, description = DEPRECATION_MESSAGE_HEADER_DESCRIPTION, schema = @Schema(type = "string")),
                @Header(name = DESCRIPTION_HEADER, description = DESCRIPTION_HEADER_DESCRIPTION, schema = @Schema(type = "string"))
        )
public @interface OkResponse 

我的 v1 映射定义类似。

【问题讨论】:

【参考方案1】:

因此,似乎在仅依赖 OpenApiCustomiser 来创建 OpenAPI 时,扫描的组件被忽略,或者至少被定制器中指定的组件覆盖(我也可以以编程方式添加我的所有架构,但是这将是非常麻烦的维护)。

将我的配置更改为以下解决了我的问题:

@Bean
public OpenAPI customOpenAPI() 
    final String securitySchemeName = "Access Token";
    return new OpenAPI()
            .addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
            .components(new Components().addSecuritySchemes(securitySchemeName, new SecurityScheme()
                    .type(SecurityScheme.Type.APIKEY)
                    .in(SecurityScheme.In.HEADER)
                    .name(HttpHeaders.AUTHORIZATION)))
            .info(apiInfo());


@Bean
public GroupedOpenApi apiV1() 
    String[] paths = "/v1/**";
    String[] packagesToScan = "com.test.controller";
    return GroupedOpenApi.builder()
            .group("v1")
            .packagesToScan(packagesToScan)
            .pathsToMatch(paths)
            .addOpenApiCustomiser(buildV1OpenAPI())
            .build();


@Bean
public GroupedOpenApi apiV2() 
    String[] paths = "/v2/**";
    String[] packagesToScan = "com.test.controller";
    return GroupedOpenApi.builder()
            .group("v2")
            .packagesToScan(packagesToScan)
            .pathsToMatch(paths)
            .addOpenApiCustomiser(buildV2OpenAPI())
            .build();


public OpenApiCustomiser buildV1OpenAPI() 
    return openApi -> openApi.info(openApi.getInfo().version("v1"));


public OpenApiCustomiser buildV2OpenAPI() 
    return openApi -> openApi.info(openApi.getInfo().version("v2"));


// Describe the apis
private Info apiInfo() 
    return new Info()
            .title("Title")
            .description("API Description.");

虽然这在技术上确实也将授权按钮和安全方案添加到 v1 组,但可以忽略它,因为这些 API 端点无论如何都不安全(内部 API,它们应该很快就会消失)。

无论如何,这可能是一个更好的解决方案,因为各组之间的信息基本相同。

【讨论】:

【参考方案2】:

您应该只修改它们,而不是创建新组件:

public OpenApiCustomiser buildV2OpenAPI() 
    final String securitySchemeName = "Access Token";
    return openApi -> 
        openApi.info(apiInfo().version("v2"))
            .addSecurityItem(new SecurityRequirement().addList(securitySchemeName));
        
        openApi.getComponents().addSecuritySchemes(securitySchemeName, new SecurityScheme()
            .type(SecurityScheme.Type.APIKEY)
            .in(SecurityScheme.In.HEADER)
            .name(HttpHeaders.AUTHORIZATION));

        return openApi;
    
    ;

【讨论】:

以上是关于以编程方式添加安全方案时,模式从组件中消失的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式向 C# 中的文件添加安全权限

虚拟桌面安全策略:网络隔离方案

如何通过 c# 以编程方式更改 COM 安全设置?

Java并发多线程编程——集合类线程不安全之HashSet的示例及解决方案

如何以编程方式向 UIStackView 添加安全区域布局?

以编程方式从模态视图控制器切换父选项卡