以编程方式添加安全方案时,架构从组件中消失
Schemas disappear from components when programmatically adding security scheme
我最近从 Springfox 转换为 Springdoc-openapi 以便为我的 Spring Boot Rest [生成我的 OpenAPI API服务。
在我添加安全方案之前,一切都运行良好。一旦我这样做了,我的方案就不再出现,并且在 SwaggerUI 页面上出现错误:
Could not resolve reference: Could not resolve pointer: /components/schemas/Ping does not exist in document
我正在以编程方式设置我的配置,并且有 2 个组。
我正在使用 Spring Boot v2.4.0 with 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 组件时没有自动扫描和添加我的方案吗?我的配置中是否缺少某些内容?
这是我在控制器中的请求映射。
@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 端点无论如何都不安全(内部 API 他们应该去无论如何很快就会离开)。
无论如何,这可能是一个更好的解决方案,因为各组之间的信息基本相同。
与其创建新组件,不如修改它们:
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;
};
}
我最近从 Springfox 转换为 Springdoc-openapi 以便为我的 Spring Boot Rest [生成我的 OpenAPI API服务。
在我添加安全方案之前,一切都运行良好。一旦我这样做了,我的方案就不再出现,并且在 SwaggerUI 页面上出现错误:
Could not resolve reference: Could not resolve pointer: /components/schemas/Ping does not exist in document
我正在以编程方式设置我的配置,并且有 2 个组。
我正在使用 Spring Boot v2.4.0 with 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 组件时没有自动扫描和添加我的方案吗?我的配置中是否缺少某些内容?
这是我在控制器中的请求映射。
@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 端点无论如何都不安全(内部 API 他们应该去无论如何很快就会离开)。
无论如何,这可能是一个更好的解决方案,因为各组之间的信息基本相同。
与其创建新组件,不如修改它们:
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;
};
}