15

SecurityScheme我对 java SpringBoot RESTful 应用程序使用 springdoc-openapi有以下定义:

    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
                .components(new Components().addSecuritySchemes("bearer-jwt",
                 new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")
                .in(SecurityScheme.In.HEADER).name("Authorization")))
                .info(new Info().title("App API").version("snapshot"));
    }

是否可以将其全局应用于所有路径,而无需在代码中的任何@SecurityRequirement位置向注释添加注释@Operation

如果是,如何将排除项添加到不安全的路径?

4

2 回答 2

26

是的,您可以在同一个地方调用addSecurityItem

  @Bean
  public OpenAPI customOpenAPI() {
    return new OpenAPI()
            .components(new Components().addSecuritySchemes("bearer-jwt",
                new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")
                    .in(SecurityScheme.In.HEADER).name("Authorization")))
            .info(new Info().title("App API").version("snapshot"))
            .addSecurityItem(
                    new SecurityRequirement().addList("bearer-jwt", Arrays.asList("read", "write")));
  }

全局安全模式可以被带有@SecurityRequirements注释的不同模式覆盖。包括删除操作的安全模式。例如,我们可以删除注册路径的安全性。

@SecurityRequirements
@PostMapping("/registration")
public ResponseEntity post(@RequestBody @Valid Registration: registration) {
    return registrationService.register(registration);
}

同时仍保留其他 API 的安全模式。

旧答案(2019 年 12 月 20 日):

全局安全模式可以被带有@SecurityRequirements注释的不同模式覆盖。但对于不安全的路径,它不能被删除。它实际上是springdoc-openapi中缺少的功能,OpenAPI 标准允许它。请参阅禁用特定操作的全局安全性

不过有一个解决方法。springdoc -openapi有一个 OpenApiCustomiser 的概念,可以用来拦截生成的模式。在定制器内部,可以以编程方式修改操作。要删除任何继承的安全性,该字段security需要设置为空数组。该逻辑可以基于任意规则,例如操作名称。我使用了标签。

定制器:

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import org.springdoc.api.OpenApiCustomiser;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Component
public class SecurityOverrideCustomizer implements OpenApiCustomiser {

    public static final String UNSECURED = "security.open";

    private static final List<Function<PathItem, Operation>> OPERATION_GETTERS = Arrays.asList(
            PathItem::getGet, PathItem::getPost, PathItem::getDelete, PathItem::getHead,
            PathItem::getOptions, PathItem::getPatch, PathItem::getPut);

    @Override
    public void customise(OpenAPI openApi) {
        openApi.getPaths().forEach((path, item) -> getOperations(item).forEach(operation -> {
            List<String> tags = operation.getTags();
            if (tags != null && tags.contains(UNSECURED)) {
                operation.setSecurity(Collections.emptyList());
                operation.setTags(filterTags(tags));
            }
        }));
    }

    private static Stream<Operation> getOperations(PathItem pathItem) {
        return OPERATION_GETTERS.stream()
                .map(getter -> getter.apply(pathItem))
                .filter(Objects::nonNull);
    }

    private static List<String> filterTags(List<String> tags) {
        return tags.stream()
                .filter(t -> !t.equals(UNSECURED))
                .collect(Collectors.toList());
    }
}

现在我们可以添加@Tag(name = SecurityOverrideCustomizer.UNSECURED)到不安全的方法:

    @Tag(name = SecurityOverrideCustomizer.UNSECURED)
    @GetMapping("/open")
    @ResponseBody
    public String open() {
        return "It works!";
    }

请记住,这只是一种解决方法。希望该问题将在下一个springdoc-openapi版本中得到解决(在撰写本文时,当前版本为 1.2.18)。

有关工作示例,请参阅springdoc-security-override-fix

于 2019-12-20T19:30:15.063 回答
5

使用 springdoc-openapi v1.2.29 测试:可以使用以下命令禁用特定端点的安全性:@SecurityRequirements

@GetMapping("/open")
@ResponseBody
@SecurityRequirements
public String open() {
    return "It works!";
}

对于旧版本,例如使用 OperationCustomizer 使用 v1.2.28 进行测试:

public static final String UNSECURED = "security.open";

@Bean
public OperationCustomizer customize() {
    return (Operation operation, HandlerMethod handlerMethod) -> {
        List<String> tags = operation.getTags();
        if (tags != null && tags.contains(UNSECURED)) {
            operation.setSecurity(Collections.emptyList());
            operation.setTags(tags.stream()
                    .filter(t -> !t.equals(UNSECURED))
                    .collect(Collectors.toList()));
        }
        return operation;
    };
}
于 2020-01-23T16:45:29.210 回答