Spring Безопасность в Spring Boot 3

В настоящее время я переношу наше приложение REST с Spring Boot 2.7.5 на 3.0.0-RC2. Я хочу, чтобы все было безопасно, кроме URL-адреса Open API. В Spring Boot 2.7.5 мы делали это:

@Named
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/openapi/openapi.yml").permitAll()
        .anyRequest().authenticated()
        .and()
        .httpBasic();
  }
}

и это работало нормально. В Spring Boot 3 мне пришлось изменить его на

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

  @Bean
  public SecurityFilterChain configure(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests((requests) -> requests
            .requestMatchers("/openapi/openapi.yml").permitAll()
            .anyRequest()
            .authenticated())
        .httpBasic();

    return http.build();
  }
}

поскольку WebSecurityConfigurerAdapter был удален. Однако это не работает. URL-адрес Open API также защищен базовой аутентификацией. Я сделал ошибку при обновлении кода или это, возможно, проблема в Spring Boot 3 RC 2?

Обновлять Поскольку большая часть нового API уже была доступна в версии 2.7.5, я обновил наш код в базе кода версии 2.7.5 следующим образом:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

  @Bean
  public SecurityFilterChain configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .authorizeHttpRequests((requests) -> requests
            .antMatchers(OPTIONS).permitAll() // allow CORS option calls for Swagger UI
            .antMatchers("/openapi/openapi.yml").permitAll()
            .anyRequest().authenticated())
        .httpBasic();
    return http.build();
  }
}

В нашей ветке для 3.0.0-RC2 код теперь такой:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

  @Bean
  public SecurityFilterChain configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .authorizeHttpRequests((requests) -> requests
            .requestMatchers(OPTIONS).permitAll() // allow CORS option calls for Swagger UI
            .requestMatchers("/openapi/openapi.yml").permitAll()
            .anyRequest().authenticated())
        .httpBasic();
    return http.build();
  }
}

Как видите, единственная разница в том, что я вызываю requestMatchers вместо antMatchers. Этот метод, кажется, был переименован. Метод antMatchers больше недоступен. Хотя конечный эффект все равно тот же. В нашей ветке для 3.0.0-RC2 Spring Boot запрашивает базовую аутентификацию для URL-адреса OpenAPI. Все еще отлично работает на 2.7.5.

Я должен, вероятно, упомянуть, что я использую Джерси. Может, это как-то связано?

Thomas Oellrich 15.11.2022 19:00

У вас действительно есть обработчик (отображение контроллера) для "/openapi/openapi.yml"? Если нет обработчика, разрешается не 404 NOT_FOUND. Что, в свою очередь, перенаправляет на /error. Поскольку /error также защищен, он попросит вас войти в систему.

Elyorbek Ibrokhimov 16.11.2022 03:28

Да. Как только я ввожу учетные данные базовой аутентификации, отображается Open API.

Thomas Oellrich 16.11.2022 06:55

Может быть матчером. Неужели нельзя сделать requests.antMatchers("/openapi/openapi.yml").permitAll()?

grekier 16.11.2022 10:03

Нет, я только что обновил вопрос. Метод antMatchers больше недоступен.

Thomas Oellrich 16.11.2022 10:11

Обновление: вы должны использовать новую версию Springdoc. springdoc.org . Сосредоточиться на тексте Для поддержки spring-boot v3 убедитесь, что вы используете springdoc-openapi v2.

Raphaël Colantonio 10.01.2023 02:18
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Версия Java на основе версии загрузки
Версия Java на основе версии загрузки
Если вы зайдете на официальный сайт Spring Boot , там представлен start.spring.io , который упрощает создание проектов Spring Boot, как показано ниже.
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
9
6
5 502
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Официальная документация предлагает пример, который я сократил здесь с вашей конфигурацией:

http
  .authorizeExchange((exchanges) ->
    exchanges
      .pathMatchers("/openapi/openapi.yml").permitAll()
      .anyExchange().authenticated())
    .httpBasic();

return http.build();

Вы можете попробовать это, поскольку он меняет «запрос» на формулировку «обмена» в соответствии с переходом на декларативные клиенты (@PostExchange vs. @PostMapping), я полагаю. Надеюсь, это поможет.

Нет, это не работает. HttpSecurity не имеет метода authorizeExchange. Ваш пример для WebFlux, который я не использую: docs.spring.io/spring-security/site/docs/current/api/org/…

Thomas Oellrich 15.11.2022 18:59

Кажется, это ошибка в Spring Boot 3. Я поднял проблему.

Я думаю, что это не так, пожалуйста, проверьте метод securityMatcher() для HttpSecurity

Sharofiddin 04.12.2022 12:31

Использовать

  http.securityMatcher("<patterns>")...

указать аутентификацию для конечных точек.

      authorizeHttpRequests((requests) -> requests
                .requestMatchers("<pattern>")

работает только для авторизации, если не установить securityMatcher , SecurityFilterChain по умолчанию получает any request для аутентификации. И любой запрос будет аутентифицирован поставщиком аутентификации.

В вашем случае вы можете определить две цепочки фильтров безопасности: одну для общедоступных конечных точек, другую для защищенных. И дайте им правильный порядок:

    @Bean
    @Order(1)
    public SecurityFilterChain configure(HttpSecurity http) throws Exception {
        http.securityMatcher(OPTIONS,"/openapi/openapi.yml").csrf().disable()
            .authorizeHttpRequests((requests) -> requests
                .anyRequest().permitAll() // allow CORS option calls for Swagger UI
    );
        return http.build();
      }
    
    @Bean
    Order(2)
      public SecurityFilterChain configure(HttpSecurity http) throws Exception {
        http.securityMatcher("/**")
            .csrf().disable()
            .authorizeHttpRequests((requests) -> requests.anyRequest().authenticated())
            .httpBasic();
        return http.build();
      }

Это кажется излишне сложным. Я согласился с предложением Энди Уилкинсона (см. ответ Джеймса Грея)

Thomas Oellrich 07.12.2022 10:03
Ответ принят как подходящий

Автор: https://github.com/wilkinsona

  @Bean
  public SecurityFilterChain configure(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((requests) -> requests
            .requestMatchers(new AntPathRequestMatcher("/openapi/openapi.yml")).permitAll()
            .anyRequest().authenticated())
        .httpBasic();
    return http.build();
  }

Источник: https://github.com/spring-projects/spring-boot/issues/33357#issuecomment-1327301183

Я рекомендую вам прямо сейчас использовать Spring Boot 3.0.0 (GA), а не версию RC.

Внутри моего WebSecurityConfig я сделал это:

private static final String[] AUTH_WHITELIST = {
        // -- Swagger UI v2
        "/v2/api-docs",
        "v2/api-docs",
        "/swagger-resources",
        "swagger-resources",
        "/swagger-resources/**",
        "swagger-resources/**",
        "/configuration/ui",
        "configuration/ui",
        "/configuration/security",
        "configuration/security",
        "/swagger-ui.html",
        "swagger-ui.html",
        "webjars/**",
        // -- Swagger UI v3
        "/v3/api-docs/**",
        "v3/api-docs/**",
        "/swagger-ui/**",
        "swagger-ui/**",
        // CSA Controllers
        "/csa/api/token",
        // Actuators
        "/actuator/**",
        "/health/**"
};

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
            .csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests( auth -> auth
                    .requestMatchers(AUTH_WHITELIST).permitAll()
                    .anyRequest().authenticated()
            )
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .httpBasic(withDefaults())
            .addFilterBefore(authenticationJwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
            //.addFilterAfter(authenticationJwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
            .build();
}

@Bean
public SecurityFilterChain configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
            .authorizeHttpRequests((requests) -> requests
                    .requestMatchers( new AntPathRequestMatcher("swagger-ui/**")).permitAll()
                    .requestMatchers( new AntPathRequestMatcher("/swagger-ui/**")).permitAll()
                    .requestMatchers( new AntPathRequestMatcher("v3/api-docs/**")).permitAll()
                    .requestMatchers( new AntPathRequestMatcher("/v3/api-docs/**")).permitAll()
                    .anyRequest().authenticated())
            .httpBasic();
    return httpSecurity.build();
}

Это и использование Dockerfile (выполнение mvn clean package и запуск .jar из Docker) избавили меня от проблем с аутентификацией внутри пользовательского интерфейса swagger.

Надеюсь, это поможет вам :)

Мой конфиг безопасности выглядит так:

Spring 3.0.0

@Bean
 public SecurityFilterChain configure(HttpSecurity http) throws Exception {
     http
             .csrf().disable()
             .authorizeHttpRequests(requests -> requests
                     .requestMatchers(HttpMethod.GET, "/", "/static/**", "/index.html", "/api/users/me").permitAll()
                     .requestMatchers(HttpMethod.POST, "/api/users").permitAll()
                     .requestMatchers(HttpMethod.GET, "/api/users/login", "/api/users/{username}", "/api/users/logout", "/api/costumers", "/api/storages").authenticated()
                     .requestMatchers(HttpMethod.POST, "/api/costumers", "/api/storages").authenticated()
                     .requestMatchers(HttpMethod.PUT, "/api/costumers/{id}", "/api/storages/{id}").authenticated()
                     .requestMatchers(HttpMethod.DELETE, "/api/users/{id}", "/api/storages/{id}", "/api/costumers/{id}").authenticated()
                     .anyRequest().denyAll())
             .httpBasic();
     return http.build();
 }

оно работает

Другие вопросы по теме