使用 spring SecurityWebFilterChain 如何 disable/block 除少数已知路径外的所有非 https 请求

Using spring SecurityWebFilterChain how to disable/block all non-https requests except few known paths

我在 Spring Boot Webflux 应用程序中使用 Spring 安全性来主要为 HTTPS 端口上的流量提供服务。然而,作为一项操作要求,我需要在我的 Spring 引导应用程序中支持几个非安全 REST API 路径以进行健康检查等,这些路径也需要在 HTTP 上公开。

那么,除了使用 SecurityWebFilterChain bean 的已知路径之外,我该如何强制执行对 HTTPS 的所有请求?

这就是我定义 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();
    }
}

这显然不会按预期工作,因为它允许所有请求使用 HTTPHTTPS 方案,而 我想始终执行 HTTPS 除了对于路径,例如/health.

请建议我需要对上述代码进行哪些更改才能完成此操作。

通过复制 HttpsRedirectWebFilter 创建自定义过滤器,在该过滤器中,如果请求的 url 不是 /health,您将修改它,使其发送 401重定向

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {    
    @Bean
    SecurityWebFilterChain springWebFilterChain( ServerHttpSecurity http )
     throws Exception {
         return http.addFilterAt(your-custom-https-filter, 
                                 SecurityWebFiltersOrder.HTTPS_REDIRECT)
                    .
                  ......
    }

    

这是我想出的办法来解决这个问题。我在 .matchers( customMatcher ) 方法

中调用自定义匹配器
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

    private static final Set<String> UNSECURED = new HashSet<>( 
             Arrays.asList ( new String[] { "/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();    
    }
}

不确定是否有更好的方法。