Springdoc-openapi возможно ли применить глобальную схему безопасности по умолчанию?

У меня есть следующий SecurityScheme с использованием springdoc-openapi для приложения Java SpringBoot RESTful:

    @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"));
    }

Можно ли применить его глобально ко всем путям, не добавляя _ 3_ аннотации к _ 4_ везде в коде?

Если да, то как добавить исключения к незащищенным путям?


person Cortlendt    schedule 16.12.2019    source источник


Ответы (2)


Да, вы можете сделать это там же, позвонив 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.

Старый ответ (20 декабря 2019 г.):

Глобальная схема безопасности может быть заменена другой схемой с аннотацией @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.

person Mafor    schedule 20.12.2019
comment
Не могли бы вы направить меня сюда: github.com/springdoc/springdoc-openapi/issues/363? - person Pra_A; 22.01.2020
comment
@PAA Вроде решено? Если нет, задайте вопрос по SA, буду рад помочь. - person Mafor; 22.01.2020
comment
@Mafor возможно ли то же самое с аннотациями? - person Robin Dijkhof; 05.01.2021

Протестировано с v1.2.29 springdoc-openapi: можно отключить безопасность для конкретной конечной точки, используя: @SecurityRequirements

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

Для более старых версий, например, протестированных с v1.2.28 с использованием OperationCustomizer:

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;
    };
}
person Community    schedule 23.01.2020