Issue
I am using Spring security within a Spring Boot Webflux application to serve traffic primarily on HTTPS
port. However as an operational requirement I need to support couple of non-secure REST API paths in my Spring Boot application for health check etc that need to be exposed on HTTP
as well.
So how do I enforce all the requests to HTTPS except for a known path using SecurityWebFilterChain
bean?
This is how I have defined my SecurityWebFilterChain
bean:
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
SecurityWebFilterChain webFilterChain( ServerHttpSecurity http )
throws Exception {
return http
.authorizeExchange(exchanges -> exchanges
.anyExchange().permitAll()
.and()
.exceptionHandling()
.authenticationEntryPoint((exchange, exception) ->
Mono.error(exception))
)
.csrf().disable()
.headers().disable()
.logout().disable()
.build();
}
}
This obviously won't work as intended because it is allowing all requests to use HTTP
and HTTPS
schemes whereas I want to always enforce HTTPS
except for a path e.g. /health
.
Please suggest what changes would I need in above code to get this done.
Solution
Here is what I came up with to to solve this problem. I am calling a custom matcher in .matchers( customMatcher )
method
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
private static final Set<String> UNSECURED =
Set.of ( "/health", "/heartbeat" );
@Bean
SecurityWebFilterChain webFilterChain( final ServerHttpSecurity http ) {
return http
.authorizeExchange(
exchanges -> exchanges
.matchers( this::blockUnsecured ).permitAll()
.and()
.exceptionHandling()
.authenticationEntryPoint(
(exchange, exception) -> Mono.error(exception))
)
.csrf().disable()
.headers().disable()
.logout().disable()
.httpBasic().disable()
.build();
}
Mono<MatchResult> blockUnsecured( final ServerWebExchange exchange ) {
// Deny all requests except few known ones using "http" scheme
URI uri = exchange.getRequest().getURI();
boolean invalid = "http".equalsIgnoreCase( uri.getScheme() ) &&
!UNSECURED.contains ( uri.getPath().toLowerCase() );
return invalid ? MatchResult.notMatch() : MatchResult.match();
}
}
Not sure if there is a better way of doing same.
Answered By - anubhava
Answer Checked By - Mary Flores (JavaFixing Volunteer)