Describe the bug After updating to the version 5.8.1, we have a problem with the AbstractSecurityInterceptor. In our code we change the strategy name at runtime, which is possible through this method. When changing the strategyName a new initialization of the SecurityContextStrategy is done. This however is not known by the AbstractSecurityInterceptor, which is retrieving the strategy once when the object is initialized here.
So if the AbstractSecurityInterceptor is initialized AFTER the change of the SecurityContextStrategy, all is fine, but if it was initialized BEFORE the change the strategy is still the old one and not the current one which then later results in an exception when trying to receive the Authentication here.
To Reproduce
Use my sample project and run the test DemoApplicationTests
There I set the strategy, create an interceptor, set another strategy and create an interceptor again.
You can see the strategies are different and so here it will have different results.
Expected behavior the strategies to be the same OR at the point the variable is used to use the strategy directly instead
Sample demo-abstractInterceptor.zip
Comment From: marcusdacoregio
Hi @huehnerlady,
By reading https://github.com/spring-projects/spring-security/issues/10973 I believe that this behavior is expected.
So if the AbstractSecurityInterceptor is initialized AFTER the change of the SecurityContextStrategy, all is fine, but if it was initialized BEFORE the change the strategy is still the old one and not the current one.
It should be possible to set the strategy in that scenario, something like:
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_GLOBAL)
val test2 = MethodSecurityInterceptor()
test2.setSecurityContextHolderStrategy(SecurityContextHolder.getContextHolderStrategy())
I do not know beforehand of a good way to set the SecurityContextHolderStrategy at runtime and have it "automatically" set for every component that uses it.
If possible, I'd like to know better your use case and understand how and why you are implementing it like this.
Comment From: huehnerlady
Hi @marcusdacoregio
I understood from #10973 that it is about the usage of the SecurityContext coming fron SecurityContextHolderStrategy instead of the SecurityContextHolder.
In this case for example we would expect something like SecurityContextHolder.getContextHolderStrategy().getContext().getAuthentication()
To me it feels odd that you would only initialize a variable with a SecurityContextHolderStrategy without making sure this strategy is concurrent with the one set in the SecurityContextHolder, which I thought was the central point of truth basically.
To our usecase: We use an autoconfiguration to set the securityContextStrategy to what we want, so instead of having to change properties in every service we use a central security lib and provide the autoconfiguration there:
@Slf4j
@Configuration
class SecurityContextHolderStrategyConfig {
SecurityContextHolderStrategyConfig() {
if (SecurityContextHolder.strategyName != MODE_INHERITABLETHREADLOCAL) {
log.info("resetting security context strategy to ${MODE_INHERITABLETHREADLOCAL}")
SecurityContextHolder.strategyName = MODE_INHERITABLETHREADLOCAL
}
}
}
Comment From: jzheaux
By default, AbstractSecurityInterceptor does the following:
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy();
So, I imagine this is something of a race condition. As stated in the documentation (**emphasis** mine):
* There are two ways to specify the desired strategy mode <code>String</code>. The first
* is to specify it via the system property keyed on {@link #SYSTEM_PROPERTY}. The second
* is to call {@link #setStrategyName(String)} **before using the class**.
If SecurityContextHolder is used before the strategy is set, then it can result in the behavior you are describing.
Typically, this means that the strategy should not be set during the application context lifecycle, or at least before Spring Security starts initializing its beans (like AbstractSecurityInterceptor).
Can you try one of these two things and let me know if they work for you:
-
Set the strategy earlier, before Spring Security starts to initialize its components. Ideally, static configuration should be done in a static context instead of Spring's application context, e.g. instead of
@Configuration, use astaticblock.If it needs to be part of the bean lifecycle, you may be able to add a
dependsOnor specify an@Orderto ensure that it happens before Spring Security, though that says nothing of third-party dependencies that might also load earlier than you and break things. -
Publish a
SecurityContextHolderStrategy@Bean. The nice thing about this is that Spring Security will look for this bean and set it on its components, includingAbstractSecurityInterceptorimplementations. It's also nice to be able to now autowire the security context. You can do it like this:
@Bean
SecurityContextHolderStrategy myStrategy() {
SecurityContextHolderStrategy strategy = new MyStrategy();
SecurityContextHolder.setContextHolderStrategy(strategy); // for parts of the application that are still referring to it statically
return strategy;
}
Comment From: huehnerlady
@jzheaux Thank you for your help, the second option does work for me however I would have expected to at least see the bean wired in the interceptor if that is the way forward?
getting it once from a static method which can be overridden at any point means it results into race conditions.
Comment From: marcusdacoregio
@huehnerlady you can follow this issue to know when that support is added.
I'm closing this as solved.