When using reactive method security on a Kotlin spring boot reactive app with coroutines, I am getting an error
The returnType class java.lang.Object on public java.lang.Object
java.lang.IllegalStateException: The returnType class java.lang.Object on public java.lang.Object nl.vintik.sample.spring.resource.HelloController.hello(kotlin.coroutines.Continuation) must return an instance of org.reactivestreams.Publisher (for example, a Mono or Flux) in order to support Reactor Context
at org.springframework.util.Assert.state(Assert.java:97) ~[spring-core-6.0.6.jar:6.0.6]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
*__checkpoint ⇢ Handler nl.vintik.sample.spring.resource.HelloController#hello(Continuation) [DispatcherHandler]
*__checkpoint ⇢ org.springframework.security.web.server.authentication.logout.LogoutWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.csrf.CsrfWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.header.HttpHeaderWriterWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
*__checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
*__checkpoint ⇢ HTTP GET "/kolin-webflux-rest/v1/hello" [ExceptionHandlingWebHandler]
I am using
@EnableReactiveMethodSecurity and @PreAuthorize
and spring boot version 3.0.4. With 3.0.3 version same issue, but 3.0.2 issues is not present and all works.
I have reproduced the issue with a hello world example:
@RestController
@RequestMapping("/v1")
class HelloController : Api {
@Operation(summary = "Get Hello")
@GetMapping("/hello")
@PreAuthorize("isAuthenticated()")
suspend fun hello(): String {
log.debug { "Hello World!" }
return "Hello World!"
}
@ExceptionHandler(org.springframework.security.access.AccessDeniedException::class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
fun handleException(accessDenied: org.springframework.security.access.AccessDeniedException) {
log.info ("API call failed", accessDenied)
}
companion object {
val log = KotlinLogging.logger {}
}
}
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
class SecurityConfig{
@Bean
fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
formLogin { disable() }
}
}
}
with gradle dependencies:
plugins {
val springVersion = "3.0.4"
val kotlinVersion = "1.8.10"
val openApiGeneratorVersion = "6.4.0"
val dependencyManagementVersion = "1.1.0"
kotlin("plugin.spring") version kotlinVersion
id("org.springframework.boot") version springVersion
kotlin("jvm") version kotlinVersion
id("io.spring.dependency-management") version dependencyManagementVersion
id("org.openapi.generator") version openApiGeneratorVersion
idea
}
group = "nl.vintik.sample.spring"
version = "0.0.1-SNAPSHOT"
configurations {
compileOnly {
extendsFrom(configurations.annotationProcessor.get())
}
}
dependencies {
val springdocVersion= "2.0.2"
val kotlinLoggingVersion = "3.0.5"
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-webflux")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
implementation("org.springdoc:springdoc-openapi-starter-webflux-ui:$springdocVersion")
implementation("io.github.microutils:kotlin-logging:$kotlinLoggingVersion")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("io.projectreactor:reactor-test")
testImplementation("org.springframework.security:spring-security-test")
}
Full public repo to reproduce the issue with integration tests: https://github.com/elenavanengelenmaslova/kotlin-webflux-rest-security-example
https://github.com/spring-projects/spring-security/issues/12857
Comment From: mhalbritter
This looks like a Spring Security issue for me. Please create an issue there, feel free to link back to this issue.
Comment From: elenavanengelenmaslova
created an issue for spring security https://github.com/spring-projects/spring-security/issues/12857