I recently migrated my Spring Boot MVC Kotlin project to Spring Boot 3. A basic auth is used for a REST controller (configured via SecurityFilterChain
- also migrated from WebSecurityConfigurerAdapter
based security). Since then, all calls to endpoints that are handled by suspend controller methods return 403. For the other endpoints it works fine.
All endpoints worked fine before the migration. When we make a controller method not suspend (using runBlocking
), it works fine.
Interesting is, that the body of the controller method is executed. I noticed, that the request is passed through the security filter chain more than once, and it is in the default AnonymousAuthenticationFilter
where it gets marked as unauthorized. The request param there does not contain the required auth headers, even if it does during the first pass through.
Some of the default HttpSecurity
logic is disabled:
http
.csrf().disable()
.headers().disable()
.sessionManagement().disable()
.securityContext().disable()
.requestCache().disable()
.servletApi().disable()
.apply(DefaultLoginPageConfigurer()).disable()
.logout().disable()
It seems that when coroutines are involved, the authorization logic is triggered twice. Once it works correctly. For the second time it fails because of missing auth headers.
I tried enabling the disabled defaults, but that did not help.
So after some more debugging and consultation with some Spring experts, I found out what was it about. It was a combination of three factors:
BasicAuthenticationFilter
is a OncePerRequestFilter
that runs only in the first filtering round - security context is not updated this time.http.securityContext().disable()
, it is missing in the second filtering round and so the request comes out as unauthorized.There are several possible solutions to this. Either disabling of the security chain processing for requests with the "ASYNC" dispatcher type, enabling to remember security context in between requests, or some other.