Spring Boot 3 Webflux/Kotlin中仅禁用指定Webhook端点CSRF防护
Spring Boot 3 Webflux/Kotlin 仅对指定端点禁用CSRF防护
问题背景
在Spring Boot 3 Webflux/Kotlin应用中,需要仅对/app/webhook端点禁用CSRF防护,但遇到以下问题:
- 无法使用
.csrf { it.ignoringAntMatchers("/app/webhook") },该方法在当前版本中不存在 - Spring默认会对所有非GET的修改型请求应用CSRF防护,导致外部提供商的Webhook调用被拦截
- 最初尝试在
csrf.requireCsrfProtectionMatcher中使用NegatedServerWebExchangeMatcher未生效
原有配置代码如下:
@Bean fun configure(http: ServerHttpSecurity): SecurityWebFilterChain { // @formatter:off return http .authorizeExchange { it.pathMatchers(GET, *ALLOW_LIST_STATIC_RESOURCES).permitAll() } .authorizeExchange { it.pathMatchers(GET, *ALLOW_LIST_VIEWS).permitAll() } .authorizeExchange { it.pathMatchers(GET, *ALLOW_LIST_RESOURCES).permitAll() } .authorizeExchange { it.pathMatchers(POST, *ALLOW_LIST_API_POST).permitAll() } .authorizeExchange { it.pathMatchers(DELETE, *ALLOW_LIST_API_DELETE).permitAll() } .formLogin { it.disable() } .headers { headers -> headers.contentTypeOptions { } } .headers { headers -> headers.frameOptions { it.mode(XFrameOptionsServerHttpHeadersWriter.Mode.DENY) } } .headers { headers -> headers.xssProtection { it.disable() } } .headers { headers -> headers.permissionsPolicy { it.policy("accelerometer=(), ambient-light-sensor=(), autoplay=(), camera=(), encrypted-media=(), fullscreen=(), geolocation=(), gyroscope=(), interest-cohort=(), magnetometer=(), microphone=(), midi=(), payment=(), usb=(), vr=(), xr-spatial-tracking=()") } } .headers { headers -> headers.contentSecurityPolicy { it.policyDirectives(contentSecurityPolicy) } } .build() // @formatter:on }
解决方法
方案1:自定义CSRF匹配规则
通过自定义requireCsrfProtectionMatcher,结合默认匹配器和否定匹配器实现对指定端点的CSRF禁用,配置后问题解决:
.csrf { csrf -> csrf.requireCsrfProtectionMatcher(disabledUrlForCSRF()) }.build() fun disabledUrlForCSRF(): AndServerWebExchangeMatcher = AndServerWebExchangeMatcher( CsrfWebFilter.DEFAULT_CSRF_MATCHER, NegatedServerWebExchangeMatcher( ServerWebExchangeMatchers.pathMatchers(POST, "/app/webhook") ) )
方案2:通过securityMatcher缩小安全规则范围
另一种可行的实现方式是通过securityMatcher指定安全规则不作用于目标端点,整理后的配置如下:
@Bean fun configure(http: ServerHttpSecurity): SecurityWebFilterChain { // @formatter:off return http .securityMatcher( isNot(ServerWebExchangeMatchers.pathMatchers(POST,"/app/webhook")) ) .authorizeExchange { it.pathMatchers(OPTIONS).permitAll() } .authorizeExchange { it.pathMatchers(POST, *ALLOW_LIST_API_POST).permitAll() } .authorizeExchange { it.pathMatchers(DELETE, *ALLOW_LIST_API_DELETE).permitAll() } .authorizeExchange { it.pathMatchers(GET).permitAll() } .authorizeExchange { it.anyExchange().denyAll() } .httpBasic { it.disable() } .formLogin { it.disable() }.securityContextRepository(NoOpServerSecurityContextRepository.getInstance()) .build() // @formatter:on } fun isNot(matcher: ServerWebExchangeMatcher): ServerWebExchangeMatcher { return NegatedServerWebExchangeMatcher(matcher) }
注意事项
- 启用CSRF时,即使配置了
NoOpServerSecurityContextRepository,仍会生成SESSION cookie - 添加所有GET端点的
permitAll()配置,可在用户访问不存在页面时重定向到自定义错误页
内容的提问来源于stack exchange,提问作者Shaunyl
相关产品推荐
相关产品推荐

