Description
Describe the bug
Working on a new backend for a product that makes heavy use of Spring with Kotlin, WebFlux, Spring Method Security, and Spring Cloud Sleuth.
Ran into an issue where Sleuth trace and span IDs were disappearing from log messages in the middle of handling requests.
After quite a bit of debugging, narrowed it down to what appears to be an issue related to reactive method security.
The context of a Kotlin coroutine is lost when execution proceeds into a suspend function annotated with @PreAuthorize
or @PostAuthorize
method security annotations.
Suspend functions that are not annotated do correctly preserve, propagate, and can access the coroutine context.
Spring Cloud Sleuth bridges between Reactor context and Kotlin coroutine context, so that is why it was being affected.
Was able to reproduce the issue without anything related to Sleuth.
Original related implementation looks to be: #9586
To Reproduce
Populate context in a @RestController
class' suspend function and call into a @Service
class' suspend function that is annotated with @PreAuthorize
. The coroutine context will be missing. When calling into a method that isn't annotated, the coroutine context is available.
Example code below. See the sample code for a complete example.
@RestController
class ExampleController(private val exampleService: ExampleService) {
@GetMapping("/test")
suspend fun endpoint(): String {
withContext(CoroutineName("methodWithoutSecurity")) {
exampleService.methodWithoutSecurity()
}
withContext(CoroutineName("methodWithPreAuthorizeSecurity")) {
exampleService.methodWithPreAuthorizeSecurity()
}
return "Test"
}
}
@Service
class ExampleService {
suspend fun methodWithoutSecurity() {
// Name will be: CoroutineName("methodWithoutSecurity")
val name = coroutineContext[CoroutineName.Key]
println("methodWithoutSecurity: name is: $name")
}
@PreAuthorize("true")
suspend fun methodWithPreAuthorizeSecurity() {
// Name will be: null
val name = coroutineContext[CoroutineName.Key]
println("methodWithPreAuthorizeSecurity: name is: $name")
}
}
Expected behavior
That suspend function coroutine context is preserved and available for suspend functions annotated with method security annotations.
Sample
https://gist.github.com/calebdelnay/7abc79922418cf914ef28eaa0d3ae368