webflux http basic только по определенному пути, в то время как все остальные используют JWT

#spring-boot #spring-security #spring-webflux

Вопрос:

У меня есть приложение spring boot, которое использует JWT для всех конечных точек. Теперь я хочу добавить /actuator конечную точку, которая использует базовую аутентификацию для включения показателей очистки prometheus.

 @EnableWebFluxSecurity
@EnableReactiveMethodSecurity
class SecurityConfig(
  val userService: UserService
) {

  @Bean
  fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? {
    return http {
      csrf { disable() }
      formLogin { disable() }
      httpBasic { disable() }
      authorizeExchange {
        authorize(ServerWebExchangeMatchers.pathMatchers(HttpMethod.OPTIONS, "/**"), permitAll)

        // the following should not use JWT but basic auth
        authorize(ServerWebExchangeMatchers.pathMatchers("/actuator"), authenticated)

        authorize(anyExchange, authenticated)
      }
      oauth2ResourceServer {
        jwt {
          jwtAuthenticationConverter = customConverter()
        }
      }
    }
  }
}
 

В стеке MVC я бы использовал что-то вроде этого:

 @EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Configuration
    @Order(1)
    public static class ActuatorWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        @Value("${management.endpoints.web.base-path}")
        private String managementPath;

        @Value("${config.actuator.user.name}")
        private String actuatorUser;

        @Value("${config.actuator.user.password}")
        private String actuatorPassword;

        @Autowired
        public void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication()
                    .withUser(actuatorUser)
                    .password(passwordEncoder().encode(actuatorPassword))
                    .authorities("ROLE_ACTUATOR");
        }

        @Bean
        public PasswordEncoder passwordEncoder() {
            return new Argon2PasswordEncoder();
        }

        protected void configure(HttpSecurity http) throws Exception {
            http.antMatcher(managementPath   "/**")
                    .cors().and()
                    .csrf().disable()
                    .authorizeRequests()
                    .anyRequest()
                    .hasRole("ACTUATOR")
                    .and()
                    .httpBasic();
        }
    }

    @Configuration
    @Order(2)
    public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
             http
                    .cors().and()
                    .csrf().disable()
                    .authenticationProvider(...)
                    .authorizeRequests()
                    // ...
        }
    }
}
 

Как это переводится в webflux?

Комментарии:

1. Вы пробовали приготовить еще один боб и заказать его?

2. @Toerktumlare Я боролся с этим, но после того, как узнал, что для этого требуется использовать securityMatcher s в webflux, это работает! подробности смотрите в моем ответе.

Ответ №1:

Добавление нескольких компонентов возможно, но требует использования securityMatcher :

 @EnableWebFluxSecurity
@EnableReactiveMethodSecurity
class SecurityConfig(
  val userService: UserService
) {
  @Value("${config.actuator.user.name}")
  private val actuatorUser: String? = null

  @Value("${config.actuator.user.password}")
  private val actuatorPassword: String? = null

  @Bean
  @Order(1)
  fun springSecurityFilterChainActuator(http: ServerHttpSecurity): SecurityWebFilterChain? {
    val encoder: PasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder()
    return http {
      securityMatcher(ServerWebExchangeMatchers.pathMatchers("/actuator/**"))
      csrf { disable() }
      formLogin { disable() }
      httpBasic {
        authenticationManager = UserDetailsRepositoryReactiveAuthenticationManager(
          MapReactiveUserDetailsService(
            User
              .withUsername(actuatorUser)
              .password(encoder.encode(actuatorPassword))
              .roles("ACTUATOR")
              .build()
          )
        )
      }
      authorizeExchange {
        authorize(anyExchange, hasRole("ACTUATOR"))
      }
    }
  }

  @Bean
  @Order(2)
  fun springSecurityFilterChainApi(http: ServerHttpSecurity): SecurityWebFilterChain? {
    return http {
      securityMatcher(ServerWebExchangeMatchers.pathMatchers("/**"))
      csrf { disable() }
      formLogin { disable() }
      httpBasic { disable() }
      authorizeExchange {
        authorize(ServerWebExchangeMatchers.pathMatchers(HttpMethod.OPTIONS, "/**"), permitAll)
        authorize(anyExchange, authenticated)
      }
      oauth2ResourceServer {
        jwt {
          jwtAuthenticationConverter = customConverter()
        }
      }
    }
  }
}