Сегодня я обновил демонстрационное приложение Webflux REST API с springboot 2.7.x до версии 3.0.0. При тестировании, найденном для вызовов POST с помощью SpringSecurity, я получаю 403 Forbidden с сообщением An expected CSRF token cannot be found. Я дважды проверил свою конфигурацию безопасности и не нашел никаких проблем.
@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();
}
Это работает в версии SpringBoot 2.7.5. Мой 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()
}
Я не вижу никаких изменений в CSRF в документации SpringSecurity.
Мой почтовый вызов,
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": "[email protected]",
"userId": "j.doe"
}'
Ответ: 403 Forbidden
An expected CSRF token cannot be found




Я испытал те же симптомы при переносе моего приложения webflux на Spring Boot 3.0.0 сегодня, которое отлично работало с 2.7.5. Итак, я погуглил «отключение csrf больше не работает» и нашел это и несколько (!) других сообщений …
Однако это было изменение аннотации Spring security 6, которое вызвало проблему: @EnableWebFluxSecurity содержал «@Configuration» в версии 5.x (я проверил), но, очевидно, больше не содержит и должен быть добавлен явно.
Таким образом, полный компонент SecurityWebFilterChain не был найден после миграции... Теперь рабочий код выглядит следующим образом:
@EnableWebFluxSecurity
@Configuration // <- this annotation was missing but worked with Spring Security 5.x
public class AccountWebSecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http,
ReactiveAuthenticationManager authenticationManager,
ServerAccessDeniedHandler accessDeniedHandler,
ServerAuthenticationEntryPoint authenticationEntryPoint) {
http.csrf().disable()
.httpBasic(httpBasicSpec -> httpBasicSpec
.authenticationManager(authenticationManager)
// when moving next line to exceptionHandlingSpecs, get empty body 401 for authentication failures (e.g. Invalid Credentials)
.authenticationEntryPoint(authenticationEntryPoint)
)
.authorizeExchange()
//...
}
Поскольку ваш фрагмент FilterChain не показывает аннотации в вашем классе, скорее всего, вы также можете пропустить @Configuration..
В моем случае теперь все работает как прежде :-)
Спасибо за Ваш ответ. Это исправило мое приложение. Мне тоже не хватало
@Configurationв моем классе конфигурации безопасности.