Spring WebFlux Security - Is it possible to configure multiple ServerAuthenticationEntryPoints on a SecurityWebFilterChain for different resources

Viewed 2939

I have a few different APIs in my spring webflux application that need to respond differently to failed authentication. I am trying to set different ServerAuthenticationEntryPoints for each API to handle these cases.

I found this example configuration that shows how to configure different AuthenticationWebFilter for different resources, which enables you to set ServerAuthenticationSuccessHandler and ServerAuthenticationFailureHandler individually, however I am not sure how to configure different ServerAuthenticationEntryPoints without having completely separate SecurityWebFilterChains.

If I have to configure separate SecurityWebFilterChains, how would I do that?

My SecurityWebFilterChain is currently configured like this - unfortunately you can't set the exceptionHandling individually and the second call to authenticationEntryPoint takes precedent:

@Bean
fun securityWebFilterChain(
    http: ServerHttpSecurity,
    userServerAuthenticationEntryPoint: ServerAuthenticationEntryPoint,
    userAuthenticationWebFilter: AuthenticationWebFilter,
    deviceServerAuthenticationEntryPoint: ServerAuthenticationEntryPoint,
    deviceAuthenticationWebFilter: AuthenticationWebFilter,
    serverSecurityContextRepository: ServerSecurityContextRepository,
    authenticationManager: ReactiveAuthenticationManager,
    serverAccessDeniedHandler: ServerAccessDeniedHandler
): SecurityWebFilterChain {
    http
        .addFilterAt(userAuthenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
        .exceptionHandling()
            .authenticationEntryPoint(userServerAuthenticationEntryPoint)
            .and()
        .authorizeExchange()
            .pathMatchers(GET, "/sign-in").permitAll()
            .pathMatchers("/authentication/**").permitAll()
            .pathMatchers(GET, "/landing").hasAnyAuthority("USER", "ADMIN")
            .pathMatchers("/user-api/**").hasAnyAuthority("USER", "ADMIN")

    http
        .addFilterAt(deviceAuthenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
        .exceptionHandling()
            .authenticationEntryPoint(deviceServerAuthenticationEntryPoint)
            .and()
        .authorizeExchange()
            .pathMatchers("/device-api/**").hasAuthority("DEVICE")

    // GLOBAL
    http
        .httpBasic().disable()
        .formLogin().disable()
        .csrf().disable()
        .cors().disable()
        .securityContextRepository(serverSecurityContextRepository)
        .authenticationManager(authenticationManager)
        .exceptionHandling()
            .accessDeniedHandler(serverAccessDeniedHandler)
            .and()
        .authorizeExchange()
            .pathMatchers(GET, "/webjars/**").permitAll()
            .pathMatchers(GET, "/assets/**").permitAll()
            .anyExchange().authenticated()

    return http.build()
}
1 Answers

It turns out the default ServerAuthenticationEntryPoint is a DelegatingServerAuthenticationEntryPoint, which enables you to configure via ServerWebExchangeMatchers which actual entry point is responsible for any given ServerWebExchange. See this comment.

Related