java - 以编程方式添加安全方案时,架构从组件中消失

标签 java spring-boot openapi springdoc

我最近从 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 组,一切正常。我的架构出现在 Swagger UI 页面上,我在生成的 api-doc 的组件部分看到它们。
    "components": {
    "schemas": {
        "ApplicationErrorResponse": {
            ...
            }
        },
        "Ping": {
            ...
        }
    }
}
对于我的 v2 组,不生成架构。
    "components": {
    "securitySchemes": {
        "Access Token": {
            "type": "apiKey",
            "name": "Authorization",
            "in": "header"
        }
    }
}
知道为什么在以编程方式将安全方案添加到 OpenAPI 组件时我的架构不会自动扫描和添加吗?我的配置中缺少什么吗?
这是我在 Controller 中的请求映射。
@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 映射的定义类似。

最佳答案

因此,似乎当仅依赖 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 端点无论如何都不 protected (内部 API 并且它们应该很快就会消失)。
无论如何可能是一个更好的解决方案,因为信息在组之间基本相同。

关于java - 以编程方式添加安全方案时,架构从组件中消失,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65331547/

相关文章:

java - 如何在 Open API 3.0 中为 GET API 定义 map 对象

java - 如何在android中正确地重用模板/xml

java - 如何从 java keystore 中选择正确的 SSL 证书,以便在相互 TLS 的情况下发送到相应的端点

rest - REST API 具有 `required` 响应属性意味着什么?

java - 使用 CorsFilter 和 spring security 时出现 Cors 错误

java - spring如何在内部使用_csrf参数或X-CSRF-TOKEN header 验证csrf token ?

node.js - 在 Swagger OpenApi 3.0 中将数组作为查询参数传递

java - Jenkins 升级问题

java - 想实现一个强化学习连接四个agent

java - (eclipse) Junit 测试不使用 VM 参数中的 cacert 证书 - 无法找到请求目标的有效证书路径