Describe the bug
I'm using Spring Boot 2.4.2 with Spring Security 5.4.2.
The application has a main context, with a WebSecurityConfigurerAdapter that contains authorization configuration for the actuators:
class SecurityConfiguration : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.csrf().disable()
.cors().and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.httpBasic().and()
.requestMatchers().antMatchers("/actuators/**").and()
.authorizeRequests()
.requestMatchers(EndpointRequest.to(CustomHealthEndpoint::class.java)).permitAll()
.requestMatchers(EndpointRequest.to(InfoEndpoint::class.java)).hasAnyRole(Roles.ONE, Roles.TWO)
.requestMatchers(EndpointRequest.to(PrometheusScrapeEndpoint::class.java)).hasAnyRole(Roles.ONE)
.requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole(Roles.THREE)
}
This works fine, so far.
It also has several child contexts, each with their own DispatcherServlet. Their URLs depend on configuration and thus are not known beforehand. I wanted to introduce an additional authorization requirement for their whole path. The code accordingly looks like this:
@Configuration
@Order(2)
class SecurityConfiguration : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.requestMatchers().antMatchers("/**").and()
.authorizeRequests()
.anyRequest().hasAnyRole(Roles.ADMIN, Roles.API)
}
}
However, while the configuration seems to be executed, it is not applied to calls. Debug log shows: Security filter chain: no match where I expected some classes. (For reference, requests to /actuators do show a a Security filter chain.)
To Reproduce * Have such a setup * Send a request to one of the child contexts without authentication
Expected behavior The request should be denied with a 401.
Sample https://github.com/fdw/spring-problem
Comment From: rwinch
@fdw Spring Security requires that the FilterChainProxy is registered with the Servlet Container. In a typical Spring Boot application, Spring Security's FilterChainProxy is registered with the Servlet Container automatially. However, due to the fact that the child ApplicationContext is setup manually it is not automatically registering the FilterChainProxy found in the child ApplicationContext. This means that, similar to how the code already exposes the DispatcherServlet, you will need to expose a Bean that registers the springSecurityFilterChain Bean with the Servlet Container. For example:
@Bean
fun springSecurityRegistration(applicationContext: WebApplicationContext, dispatcher: ServletRegistrationBean<DispatcherServlet>): FilterRegistrationBean<Filter> {
val springSecurityFilterChain = DelegatingFilterProxy(DEFAULT_FILTER_NAME, applicationContext)
return FilterRegistrationBean(springSecurityFilterChain, dispatcher)
}
I sent a Pull Request that contains a few tests and the fix.
NOTE For what it is worth, I'd recommend looking into different ways of solving your problem rather than creating multiple ApplicationContexts. More than one context really makes your life much more difficult and often will have extra overhead (i.e. more Beans being created which means more memory consumption).
Comment From: fdw
Thank you very much for your help! I really appreciate it :)
Unfortunately, I couldn't get your fix to work. I've added some tests, and it seems to me that in one child context, all requests are allowed (whether authorized or not), and in the other, all are denied (both authorized and not). Do you have any idea why that happens?
Comment From: rwinch
I've updated the branch of the pull request with small commits and comments about what needed to be changed.
However, I would reiterate that I would consider trying to accomplish what you want a different way. In my experience, using multiple ApplicationContexts is rarely the best answer.
Comment From: fdw
Thank you so much, that worked!
However, I would reiterate that I would consider trying to accomplish what you want a different way.
Believe me, I'm with you. We just haven't found another way yet. Our service offers onle a few capabilities, but for different subjects, so we thought encapsulating the capabilities in a Spring context and having multiple, similar contexts was a good idea. To add to that, the subjects themselves are configurable, so we can't really hardcode anything.
Comment From: joris-lambrechts
@fdw is there any way you could share the changes from the pull request?
From what I gather after some hours, defining a FilterRegistrationBean is not possible inside the child context. The filters and servlets are registered with Tomcat before the child context is created. Adding a FilterRegistrationBean for filter FilterChainProxy gets me only so far. What I'm stuck on is getting the SecurityFilterChains from the child context in that FilterChainProxy. I tried it with a mutable list and a custom FilterChainProxy, it works but I don't really like the mutability of the list.
I'm migrating a Spring application to Spring Boot and running into the same problem. Merging all the ApplicationContexts to one is currently too big of a change. Also, the version I'm going with is Spring Boot 1.5.9 as that contains the same Spring version as the project now has. Once that all works, we will migrate to the latest 2.x.
Comment From: fdw
I'm truly sorry, but I don't have access to the code anymore 🙁
Comment From: joris-lambrechts
@fdw don't be and thanks for taking the time to answer. I found a solution but I'm not satisfied with it. I also realized after Rob's comment about multiple contexts, I don't need it in this case. The final result is that all the security-related configuration is part of the parent context.