Spring Boot 3 Webflux中Spring Security禁用CSRF失效问题求助
问题描述
将Webflux REST API演示应用从Spring Boot 2.7.x版本升级至3.0.0后,测试POST请求时遇到Spring Security返回403 Forbidden错误,提示An expected CSRF token cannot be found。原有安全配置在Spring Boot 2.7.5中可正常工作,升级后失效。
安全配置代码
@Bean public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { return http .csrf().disable() .authorizeExchange() .pathMatchers(HttpMethod.GET, "/actuator/**").permitAll() .pathMatchers(HttpMethod.POST, "/api/v1/users", "/api/v1/users/**").hasRole(ReactiveConstant.SECURITY_ROLE_ADMIN) // Only admin can do POST .pathMatchers(HttpMethod.GET, "/api/v1/users", "/api/v1/users/**").hasAnyRole(ReactiveConstant.SECURITY_ROLE_USER, ReactiveConstant.SECURITY_ROLE_ADMIN) // user can only do GET .anyExchange().authenticated() .and().formLogin() .and().httpBasic() .and().formLogin().disable() .build(); }
build.gradle 内容
plugins { id 'org.springframework.boot' version '3.0.0' id 'io.spring.dependency-management' version '1.1.0' id 'java' id 'groovy' } group = 'io.c12.bala' version = '0.2.1' sourceCompatibility = JavaVersion.VERSION_17 configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenLocal() mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-mongodb-reactive' implementation 'org.springframework.boot:spring-boot-starter-webflux' implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-security' // Springboot utils implementation 'io.projectreactor:reactor-tools' // For Reactor debugging in IDE compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' implementation 'org.modelmapper:modelmapper:3.1.0' implementation 'io.netty:netty-resolver-dns-native-macos:4.1.85.Final:osx-aarch_64' // For macos netty DNS issue. implementation 'com.aventrix.jnanoid:jnanoid:2.0.0' // Springboot testing with Spock test framework testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' // Spock test framework testImplementation 'org.spockframework:spock-core:2.3-groovy-4.0' testImplementation 'org.spockframework:spock-spring:2.3-groovy-4.0' // Reactor test framework testImplementation 'io.projectreactor:reactor-test' } test { useJUnitPlatform() maxParallelForks = Runtime.runtime.availableProcessors() }
测试请求与返回
测试POST请求:
curl --location --request POST 'http://localhost:8080/api/v1/users' \ --header 'Authorization: Basic am9objpIZWxsb1dvcmxkQDEyMw==' \ --header 'Content-Type: application/json' \ --data-raw '{ "firstName": "John", "lastName": "Doe", "emailId": "John.doe@example.com", "userId": "j.doe" }'
返回结果:
403 Forbidden An expected CSRF token cannot be found
解决方案
问题根源在于Spring Boot 3.0对应的Spring Security 6.0对WebFlux的SecurityWebFilterChain链式调用逻辑做了调整,原有.and()链式写法会导致csrf().disable()的配置被后续操作覆盖,最终CSRF保护并未真正关闭。
修改安全配置为Spring Security 6推荐的lambda风格,确保每个配置独立生效:
@Bean public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { return http // 先配置权限规则 .authorizeExchange(exchanges -> exchanges .pathMatchers(HttpMethod.GET, "/actuator/**").permitAll() .pathMatchers(HttpMethod.POST, "/api/v1/users", "/api/v1/users/**").hasRole(ReactiveConstant.SECURITY_ROLE_ADMIN) .pathMatchers(HttpMethod.GET, "/api/v1/users", "/api/v1/users/**").hasAnyRole(ReactiveConstant.SECURITY_ROLE_USER, ReactiveConstant.SECURITY_ROLE_ADMIN) .anyExchange().authenticated() ) // 明确关闭CSRF保护 .csrf(csrf -> csrf.disable()) // 启用HTTP Basic认证 .httpBasic(Customizer.withDefaults()) // 禁用表单登录 .formLogin(formLogin -> formLogin.disable()) .build(); }
关键说明
- 使用lambda表达式替代
.and()链式调用,避免配置被意外覆盖; - 调整配置顺序,优先定义权限规则,再配置CSRF、认证方式等;
- 所有配置项通过lambda明确指定,逻辑更清晰,符合Spring Security 6的设计规范。
内容的提问来源于stack exchange,提问作者Bala
相关产品推荐
相关产品推荐

