WebSecurityConfigurerAdapter - invalid bean definition
chriswhite199 opened this issue · 2 comments
is there any advice for configuration ordering when trying to define you own WebSecurityConfigurerAdapter, as per the docs?
I get the following error when trying to create a WebSecurityConfigurerAdapter:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'saml' defined in com.github.ulisesbocchio.spring.boot.security.saml.configurer.WebSecurityConfigurerAdapterTest$MyWebSecurityConfigurerAdapter: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=webSecurityConfigurerAdapterTest.MyWebSecurityConfigurerAdapter; factoryMethodName=saml; initMethodName=null; destroyMethodName=(inferred); defined in com.github.ulisesbocchio.spring.boot.security.saml.configurer.WebSecurityConfigurerAdapterTest$MyWebSecurityConfigurerAdapter] for bean 'saml': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=com.github.ulisesbocchio.spring.boot.security.saml.configuration.SAMLServiceProviderSecurityConfiguration$SAMLWebSecurityConfigurer; factoryMethodName=saml; initMethodName=null; destroyMethodName=(inferred); defined in com.github.ulisesbocchio.spring.boot.security.saml.configuration.SAMLServiceProviderSecurityConfiguration$SAMLWebSecurityConfigurer] bound.
at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:897)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(ConfigurationClassBeanDefinitionReader.java:274)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:141)
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:117)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:327)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:232)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:275)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:95)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:705)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:531)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:127)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
... 32 more
Here's a sample unit test that replicates
package com.github.ulisesbocchio.spring.boot.security.saml.configurer;
import com.github.ulisesbocchio.spring.boot.security.saml.annotation.EnableSAMLSSO;
import com.github.ulisesbocchio.spring.boot.security.saml.bean.SAMLConfigurerBean;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
/**
* @author Ulises Bocchio
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = WebSecurityConfigurerAdapterTest.MyWebSecurityConfigurerAdapter.class)
public class WebSecurityConfigurerAdapterTest {
@SpringBootApplication
@EnableSAMLSSO
public static class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Bean
SAMLConfigurerBean saml() {
return new SAMLConfigurerBean();
}
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.httpBasic()
.disable()
.csrf()
.disable()
.anonymous()
.and()
.apply(saml())
.and()
.authorizeRequests()
.requestMatchers(saml().endpointsMatcher())
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
@Test
public void contextLoads() throws Exception {
}
}
To work around this, if you move the @EnableSAMLSSO to another configuration class, that is lexicographically greater than the config for the adapter, it works:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = {
WebSecurityConfigurerAdapterTest.MyWebSecurityConfigurerAdapter.class,
WebSecurityConfigurerAdapterTest.ZSSOConfig.class
})
public class WebSecurityConfigurerAdapterTest {
@SpringBootApplication
public static class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Bean
SAMLConfigurerBean saml() {
return new SAMLConfigurerBean();
}
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.httpBasic()
.disable()
.csrf()
.disable()
.anonymous()
.and()
.apply(saml())
.and()
.authorizeRequests()
.requestMatchers(saml().endpointsMatcher())
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
@EnableSAMLSSO
public static class ZSSOConfig {
}
@Test
public void contextLoads() throws Exception {
}
}
No amount of using [@]Order, implements Ordered or AutoConfigureXXX seems to resolve this without naming the second class accordingly.
I'm thinking this would be resolved by explicit AutoConfiguration of SAMLServiceProviderSecurityConfiguration in META-INF/spring.factories, as the ConditionalOnMissingBeans would be invoked after any other user defined [@]Configuration classes
One solution i've found is to use AutoConfiguration vs @EnableSAMLSSO
.
Taking the first unit test from my previous post, if you comment out the @EnableSAMLSSO
and instead add a META-INF/spring.factories
file with the following contents, the test now passes:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.github.ulisesbocchio.spring.boot.security.saml.configuration.SAMLServiceProviderSecurityConfiguration
This does mean that you can't easily enable / disable without changing this file, but i'm assuming that a simple wrapper AutoConfiguration class (outside the scanning classpath) with a @ConditionalOnProperty should work.
So taking this to its logical conclusion:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.github.ulisesbocchio.spring.boot.security.saml.configurer.WebSecurityConfigurerAdapterTest.SamlAutoConfiguration
package com.github.ulisesbocchio.spring.boot.security.saml.configurer;
import com.github.ulisesbocchio.spring.boot.security.saml.annotation.EnableSAMLSSO;
import com.github.ulisesbocchio.spring.boot.security.saml.bean.SAMLConfigurerBean;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = {
WebSecurityConfigurerAdapterTest.MyWebSecurityConfigurerAdapter.class
},
properties = {
"spring.security.saml.enabled=true"
})
public class WebSecurityConfigurerAdapterTest {
@SpringBootApplication
public static class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Bean
SAMLConfigurerBean saml() {
return new SAMLConfigurerBean();
}
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.httpBasic()
.disable()
.csrf()
.disable()
.anonymous()
.and()
.apply(saml())
.and()
.authorizeRequests()
.requestMatchers(saml().endpointsMatcher())
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
@ConditionalOnProperty(value = "spring.security.saml.enabled", havingValue = "true")
@EnableSAMLSSO
public static class SamlAutoConfiguration {
}
@Test
public void contextLoads() throws Exception {
}
}
Hi i am also facing the same problem
org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'saml' defined in com.spring.MainApplication: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=mainApplication; factoryMethodName=saml; initMethodName=null; destroyMethodName=(inferred); defined in com.spring.MainApplication] for bean 'saml': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=com.github.ulisesbocchio.spring.boot.security.saml.configuration.SAMLServiceProviderSecurityConfiguration$SAMLWebSecurityConfigurer; factoryMethodName=saml; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/github/ulisesbocchio/spring/boot/security/saml/configuration/SAMLServiceProviderSecurityConfiguration$SAMLWebSecurityConfigurer.class]] bound.
at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:927) ~[spring-beans-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(ConfigurationClassBeanDefinitionReader.java:287) ~[spring-context-5.2.4.RELEASE
If i use ServiceProviderConfigurerAdapter method, the Configurer parameter does not seems to be set.
The project i working on have an existing WebSecurityConfigurerAdapter, is there anyway around this?