使用 spring SecurityWebFilterChain 如何禁用/阻止除少数已知路径外的所有非 https 请求
Posted
技术标签:
【中文标题】使用 spring SecurityWebFilterChain 如何禁用/阻止除少数已知路径外的所有非 https 请求【英文标题】:Using spring SecurityWebFilterChain how to disable/block all non-https requests except few known paths 【发布时间】:2020-11-06 04:48:20 【问题描述】:我在 Spring Boot Webflux 应用程序中使用 Spring 安全性主要在 HTTPS
端口上提供流量。但是,作为一项操作要求,我需要在我的 Spring Boot 应用程序中支持几个非安全 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();
这显然不会按预期工作,因为它允许所有请求使用 HTTP
和 HTTPS
方案,而 我希望始终强制执行 HTTPS
,路径除外,例如/health
.
请建议我需要对上述代码进行哪些更改才能完成此操作。
【问题讨论】:
【参考方案1】:这是我想出解决这个问题的方法。我在.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();
不确定是否有更好的方法。
【讨论】:
我认为我对你的问题的理解是完全错误的。如果我错了,请纠正我,因为我不知道 webflux,如果有人通过 https 连接,他们不需要提供任何登录详细信息,他们可以访问系统。对吗? 是的,这是正确的,没有登录,因为此应用程序正在使用双向 TLS,并且客户端正在提供有效的密钥/证书对进行连接。 在我看来,我根本不会使用 spring security 来检查只允许通过 http 的一些 url。我会使用简单的servlet filter
或 interceptor
(我不知道它在 webflux 中叫什么,但始终扮演相同的角色)并精确检查您在自定义匹配器中所做的事情。感觉就像买车拆了才得到轮胎
太棒了。对我也有好处,因为我被介绍了 web Flux spring 安全
虽然这似乎可行,但这段代码几乎没有问题。 .contains
是 endsWith
的糟糕替代品。有人可能会在调试时扯掉他的头发;)另外,getPath().toLowerCase
意味着 /abc
和 /ABC
将被视为相同的 URI。 URI 区分大小写。正如我所建议的,您应该只使用执行器并在内部路由请求,同时保持整个网站仅服务于 https 方案。当您提供混合内容时,对 SEO 排名也有很大影响。【参考方案2】:
通过复制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)
.
......
【讨论】:
感谢您的回答。但是Javadoc of ServerHttpSecurity 没有requiresChannel()
方法。
没关系。反馈是任何形式的反馈。有一个.redirectToHttps()
,如果你使用那一秒过滤器会起作用吗?
是的,我玩过.redirectToHttps()
。它执行302
重定向,这不是所需的行为。我更愿意发回 500
或 401
以进行不安全访问。
因此,如果它强制执行https
,但它通过重定向来实现,而不是使用它,您可以通过复制HttpsRedirectWebFilter
的代码来创建自定义过滤器。在该自定义过滤器中,如果不是 https,而不是重定向,您可以发送 401
请分享所有相关代码,以便我查看。以上是关于使用 spring SecurityWebFilterChain 如何禁用/阻止除少数已知路径外的所有非 https 请求的主要内容,如果未能解决你的问题,请参考以下文章
Spring学习-----Spring使用@Autowired注解自动装配
Spring Framework,Spring Security - 可以在没有 Spring Framework 的情况下使用 Spring Security?