We are in the process of switching a regular spring application over to spring-boot, but we hit a small problem while transfering to spring-boot.
We have defined a Custom RequestDataValueProcessor which is being overriden by spring-security.
In our own application, before using spring-boot, we were able to overcome this problem by defining this bean in a different location. The problem we are facing now is that spring-boot executes certain configurations at a later time, which makes it impossible for us to override the behaviour again.
In our console log we see a message like this:
2015-12-04 10:49:28.120 INFO 7584 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean 'requestDataValueProcessor' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=demoApplication; factoryMethodName=requestDataValueProcessor; initMethodName=null; destroyMethodName=(inferred); defined in com.example.DemoApplication] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration; factoryMethodName=requestDataValueProcessor; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration.class]]
I have added a simple demo application where you can see this behaviour. The href on the anchor(a) tag on the index page should contain "OVERRIDEN ACTION" instead of the default "/" that was defined in the page itself.
For this example spring-security is enabled, the username="user", the password is generated in the console.
Comment From: SnakeSVx
Demo file, since github would not let me upload it. https://www.wetransfer.com/downloads/f9350a835f6573eb7788ff6cd164f82720151204100358/1017bee3b9dc37c09a54320db35e437c20151204100358/60f3ba
Comment From: philwebb
One way that might work is to use a BeanDefinitionRegistryPostProcessor. Try replacing the requestDataValueProcessor @Bean method in your config with this:
@Bean
public static BeanDefinitionRegistryPostProcessor requestDataValueProcessorPostProcessor() {
return new BeanDefinitionRegistryPostProcessor() {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
registry.removeBeanDefinition("requestDataValueProcessor");
registry.registerBeanDefinition("requestDataValueProcessor",
new RootBeanDefinition(CustomRequestDataValueProcessor.class));
}
};
}
Comment From: philwebb
@rstoyanchev @rwinch Is there a better way to do this?
Comment From: rwinch
This is related to SEC-3063. The current mechanism for overriding the requestDataValueProcessor Bean is to ensure that the Bean is defined in the class annotated with @EnableWebSecurity. This ensures that Spring Security's requestDataValueProcessor is created first and thus overridden.
I'd actually be interested if @rstoyanchev has a better approach to Spring Security providing a default RequestDataPostProcessor.
Comment From: rstoyanchev
@SnakeSVx can you clarify the purpose of your custom RequestDataValueProcessor? In other words is the intent to replace/customize the CsrfRequestDataValueProcessor from Spring Security? Do you want them both to work? Or do you not care if CsrfRequestDataValueProcessor is there or not?
Comment From: SnakeSVx
@rstoyanchev At the moment we are using our custom RequestDataValueProcessor for rewriting url's to make sure they are compatible with our proxy configuration and to replace certain keywords in url's to go to external url's. At the moment we are also not using the CsrfRequestDataValueProcessor (but if we ever do it seems easy enough for us to just extend it or write our own RequestDataValueProcessor that combines the two). So we don't need to have them both working at the same time.
One thing we currently are trying out (and so far seems to be working) is using auto-configuration to register our own RequestDataValueProcessor after the SecurityAutoConfiguration has run.
But since we are not sure if this will keep working in future versions, it would always be nice to know if this is the way to go, or if an easier (preferably integrated spring-boot) solution would be possible.
Comment From: wilkinsona
so far seems to be working) is using autoconfiguration to register our own RequestDataValueProcessor after the SecurityAutoConfiguration has run.
Given the way things are at the moment, that's a good approach. I assume you're using @AutoConfigureAfter to get the ordering right? We use this sort of ordering in numerous places so it's safe to assume that it'll work for the foreseeable future.
I agree it'd be nice to have an "official" way to do this. Using @EnableWebSecurity isn't ideal in a Boot app as it'll switch off some of Boot's Security auto-configuration.
Comment From: SnakeSVx
Yes, at the moment we are using 2 conditionals. The first checks if the CsrfRequestDataValueProcessor is on the classpath, the second one is used to make sure we only auto-configure after SecurityAutoConfiguration.
Comment From: rstoyanchev
What about adding @Primary as suggested in https://jira.spring.io/browse/SEC-3062?
Comment From: rwinch
@rstoyanchev Unfortunately I have since discovered that @Primary does not prevent a bean from being overridden if it has the same bean name. The test I had ran to check this coincidentally loaded the @Primary second.
Comment From: rstoyanchev
Right that makes sense of course. Perhaps update https://jira.spring.io/browse/SEC-3062 which still suggests that as a workaround.
Comment From: rwinch
Good idea @rstoyanchev. I have updated the ticket
Comment From: shainegordon
I am using the BeanDefinitionRegistryPostProcessor method as described by @philwebb to get around this issue too, on Spring Boot 1.4
The reason that we need a custom RequestDataValueProcessor is to add an additional hidden field to our forms for "session conversations (multiple browser tabs accessing not conflicting with each other when using @SessionAttributes), which is used by a custom SessionAttributeStore
Comment From: wilkinsona
It looks like we'll need some changes in Spring Security to be able to do something about this. Do you agree, @rwinch?
Comment From: rwinch
@wilkinsona I'm open to suggestions, but as far as I am aware there really isn't much Spring Security can do. It needs to work within the constraints of standard Spring which does not allow for conditional creation of beans.
Comment From: wilkinsona
I think we need some indirection between the publication of the bean and what it is that gets published. I wonder if it would help if Spring Security provided a hook point for creating a custom RequestDataValueProcessor that, by default, would create a CsrfRequestDataValueProcessor? Something akin to createRequestMappingHandlerMapping() on WebMvcConfigurationSupport. We'd still need to figure out how to get it to work without @EnableWebSecurity switching everything off, but it feels like it may be a step in the right direction.
Comment From: rwinch
I think that any layers of indirection probably belong in Spring MVC rather than Spring Security because this is not a Spring Security specific concern. Once they are created within Spring MVC, Spring Security could leverage them.
Comment From: rstoyanchev
The Spring Framework currently does not create any instances and does not have implementations of RequestDataValueProcessor. It simply looks for a bean by type and well-known name. What do you have in mind for the Spring Framework side to do differently?
Comment From: rwinch
@rstoyanchev Thanks for the response. Perhaps it is best to start back where we left off. What it the recommended way for Spring Security to provide a default RequestDataValueProcessor that can be overridden?
Comment From: snicoll
@rstoyanchev @rwinch can we resume the discussion on this one please?
Comment From: snicoll
On a second thought, the vastly simplified auto-configuration for Spring Security in 2.0 shouldn't cause any Spring Boot specific issue anymore. We can reopen if that turns out not to be the case.
Comment From: chrisgolle
i find this is an issue. I am using spring boot 2.2.0. I am defining my own request data value processor, however, it conflicts with the existing one from WebMvcSecurityConfiguration. I workaround this by spring.main.allow-bean-definition-overriding=true which I would rather not have.
Comment From: wilkinsona
@chrisgolle WebMvcSecurityConfiguration is part of Spring Security so there's nothing that we can do here to address your problem. You may want to chat with the Spring Security community on Gitter or open a Spring Security issue if there's an improvement that you'd like to see.