SpringBoot Webflux — как игнорировать запросы с пустыми заголовками авторизации

В настоящее время я использую довольно простой подход для ограничения определенного suburl (все в /api/rest) и всех его подпутей через WebFluxSecurity. Некоторые пути (все прямо под корнем НЕ в /api/rest) исключены, чтобы к ним можно было получить доступ без авторизации. Однако иногда запрашивающая сторона может отправить пустой заголовок авторизации, что приводит к тому, что незащищенные конечные точки возвращают ошибку 401.

См. соответствующий код здесь:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfiguration {

    @Value(value = "${...}")
    private String user;
    @Value(value = "${...}")
    private String pw;

    @Bean
    public MapReactiveUserDetailsService userDetailsService() {
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();

        UserDetails user = User
                .withUsername(user)
                .password(encoder.encode(pw))
                .roles("USER")
                .build();

        return new MapReactiveUserDetailsService(user);
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http
                .authorizeExchange(exchanges -> exchanges
                        .pathMatchers("/api/rest/**")
                        .authenticated()
                        .anyExchange()
                        .permitAll()
                )
                .httpBasic(withDefaults());
        return http.build();
    }
}

В stackoverflow я нашел только несколько предложений, как справиться с этим с помощью WebSecurity. Однако для меня это невозможно, так как я использую безопасность webflux.

См., например.

Springboot webflux выдает 401, когда заголовок авторизации отправляется на неограниченную конечную точку

Spring Boot 2: Basic Http Auth заставляет незащищенные конечные точки отвечать 401 «Unauthorized», если заголовок авторизации прикреплен

Вы упоминаете передачу пустого заголовка Authorization. Я не могу воспроизвести поведение, которое вы описываете, и просмотрев код для ServerHttpBasicAuthenticationConverter, который читает заголовок, он не выглядит так, как будто пустое значение заголовка вызовет ошибку 401. Можете ли вы предоставить пример запроса, который воспроизводит эту проблему?

Steve Riesenberg 03.02.2023 16:48

Может быть, проще воспроизвести: это не просто пустой заголовок аутентификации, который создает 401. Даже вызов конечных точек, которые должны совпадать с .anyExchange().permitAll() (то есть незащищенные конечные точки) с неправильной аутентификацией (например, неверный пароль), приводит к 401. Насколько я понимаю это происходит из-за .authorizeExchange, который выполняет сопоставление с ReactiveUserDetails.

jju-untis 06.02.2023 09:42
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
2
68
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

TL;DR

Если вы передадите неверные учетные данные любой конечной точке с включенным httpBasic(), она вернет ответ 401.

Здесь важно различать аутентификацию и авторизацию. Метод httpBasic() DSL добавляет AuthenticationWebFilter, настроенный для HTTP Basic. Метод authorizeExchange(...) DSL определяет правила авторизации, такие как authenticated() и permitAll().

Фильтр аутентификации появляется в цепочке фильтров Spring Security раньше, чем фильтр авторизации, поэтому аутентификация происходит первой, как мы и ожидали. Судя по вашим комментариям, вы ожидаете, что аутентификация не произойдет, если вы пометите конечную точку как permitAll(), но это не так.

Будет ли на самом деле предприниматься попытка проверки подлинности для определенного запроса, зависит от того, как фильтр проверки подлинности соответствует запросу. В случае AuthenticationWebFilterServerWebExchangeMatcher (requiresAuthenticationMatcher) определяет, требуется ли аутентификация. Для httpBasic() каждый запрос требует аутентификации. Если вы передадите неверные учетные данные любой конечной точке с включенным httpBasic(), она вернет ответ 401.

Кроме того, ServerAuthenticationConverter (authenticationConverter) используется для чтения заголовка Authorization и анализа учетных данных. Это то, что потерпит неудачу, если будет указан недопустимый токен (или заголовок Authorization). ServerHttpBasicAuthenticationConverter используется для httpBasic() и довольно прощает недопустимые значения заголовка. Я не нахожу никаких сценариев, которые терпят неудачу и дают ответ 401, за исключением недействительных учетных данных.

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