I have a concrete situation where I want to exclude the configuration implemented by
org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration$JwtTokenServicesConfiguration
I tried both exclude = {ResourceServerTokenServicesConfiguration.class} and excludeName = {"org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration$JwtTokenServicesConfiguration"} but none is working.
The scenario is that I want to implement my own ResourceServerTokenServices but when the upstream beans have to autowire this bean, I get this error:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.security.oauth2.provider.token.TokenStore] is defined: expected single matching bean but found 2: customJwtTokenStore,jwtTokenStore
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1126) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
... 19 common frames omitted
Clearly, the boot JwtTokenServicesConfiguration configuration is not omitted. There would be a hack here where I can make the JwtTokenCondition fail, but this is a low hack.
Here is the simple configuration that I'm using:
@SpringBootApplication
@EnableAutoConfiguration(exclude = {ResourceServerTokenServicesConfiguration.class},
excludeName = {"org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerTokenServicesConfiguration$JwtTokenServicesConfiguration"})
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true, proxyTargetClass = true)
public class Elephant
{
public static void main(String[] args) {
SpringApplication.run(Elephant.class, args);
}
@Configuration
@EnableResourceServer
protected static class SecurityConfiguration extends ResourceServerConfigurerAdapter
{
@Autowired
@Qualifier("customJwtTokenEnhancer")
JwtAccessTokenConverter customJwtTokenEnhancer;
@Autowired
@Qualifier("customJwtTokenStore")
TokenStore customJwtTokenStore;
@Autowired
@Qualifier("customJwtTokenServices")
ResourceServerTokenServices customJwtTokenServices;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception
{
resources.tokenServices(customJwtTokenServices);
}
@Bean
public ResourceServerTokenServices customJwtTokenServices() {
DefaultTokenServices services = new DefaultTokenServices();
services.setTokenStore(customJwtTokenStore);
return services;
}
@Bean
public TokenStore customJwtTokenStore() {
return new JwtTokenStore(customJwtTokenEnhancer);
}
@Bean
@Autowired
public JwtAccessTokenConverter customJwtTokenEnhancer(
@Value("${security.oauth2.resource.jwt.keyValue}") String keyValue) {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter(){
@Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map)
{
OAuth2Authentication authentication = super.extractAuthentication(map);
Map<String, String> details = new HashMap<>();
details.put("account_id", (String) map.get("account_id"));
authentication.setDetails(details);
return authentication;
}
};
if (keyValue != null) {
converter.setVerifierKey(keyValue);
}
return converter;
}
}
}
this is the configuration file:
server.port = 8081
logging.level.org.springframework.security=DEBUG
security.sessions=stateless
security.oauth2.resource.jwt.keyValue=-----BEGIN PUBLIC KEY-----[[MY KEY]]-----END PUBLIC KEY-----
and the dependencies:
dependencyManagement {
imports {
mavenBom "org.springframework.boot:spring-boot-starter-parent:1.3.3.RELEASE"
}
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-jdbc')
compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-web')
compile('net.sf.ehcache:ehcache:2.10.1')
compile("org.springframework.security:spring-security-acl:4.0.3.RELEASE")
compile('org.springframework.security.oauth:spring-security-oauth2:2.0.9.RELEASE')
compile('org.springframework.security:spring-security-jwt')
compile('org.json:json:20150729')
compile('org.apache.commons:commons-lang3:3.1')
runtime('mysql:mysql-connector-java')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.testng:testng:6.9.8')
testCompile('org.hamcrest:hamcrest-all:1.3')
}
Comment From: snicoll
You can't. Exclude only works on auto-configuration classes (top level classes) referenced in the documentation.
As for the concrete problem, I am sure @dsyer knows more than I am.
Comment From: dsyer
I guess I don't see a problem with making JwtTokenCondition fail (that's what it's there for). An alternative would be to not create a @Bean for your TokenStore, but I think that would just lead to issues later.
Comment From: nucatus
This is what I actually did in the end.
But that is "all or nothing" in terms of what Spring Boot can do for you in the future. Maybe there is a need for finer control on what Spring boot auto-config capabilities can be turned on and off. And then, this breaks the standardization of the configuration. Again, this could be problematic in the future.
The side story is that I needed this to address this issue (714).
I'm not the expert in this case, so I'm just posting my concerns. Thanks for the advice though.
Comment From: dfernandezm
+1 for a way of disabling autoconfiguration in inner classes. I wanted to do the same (disable inner class in Jackson Joda Time autoconfiguration) and ended up workarounding it by not customizing the configuration.
Comment From: philwebb
We can't easily disable inner-configurations. I'd rather take each need on a case-by-case basis and work out why a specific inner-configuration couldn't be used. @dfernandezm Feel free to raise a new issue if you think there is some additional @Condition guard that we need for Jackson + Joda Time.
Comment From: philwebb
We're not keen to increase the complexity of excludes. The inner configs are really meant as an implementation detail.
Comment From: espre05
Is there a way to specify package name for exclude?
Would be easy if there is an option to specify "org.apache.camel" in below case. e.g: packckageExclude={"org.apache.camel"}
@SpringBootApplication(exclude = {
org.apache.camel.spring.boot.security.CamelSSLAutoConfiguration.class
, org.apache.camel.component.bean.springboot.BeanComponentAutoConfiguration.class
,org.apache.camel.component.beanclass.springboot.ClassComponentAutoConfiguration.class
, org.apache.camel.component.binding.springboot.BindingNameComponentAutoConfiguration.class
,org.apache.camel.component.browse.springboot.BrowseComponentAutoConfiguration.class
,org.apache.camel.component.controlbus.springboot.ControlBusComponentAutoConfiguration.class
, org.apache.camel.component.dataformat.springboot.DataFormatComponentAutoConfiguration.class
,org.apache.camel.component.dataset.springboot.DataSetComponentAutoConfiguration.class
,org.apache.camel.component.direct.springboot.DirectComponentAutoConfiguration.class
,org.apache.camel.component.directvm.springboot.DirectVmComponentAutoConfiguration.class
,org.apache.camel.spring.boot.health.HealthCheckRoutesAutoConfiguration.class
,org.apache.camel.component.file.springboot.FileComponentAutoConfiguration.class
, org.apache.camel.component.jackson.springboot.JacksonDataFormatAutoConfiguration.class
,org.apache.camel.component.jpa.springboot.JpaComponentAutoConfiguration.class
,org.apache.camel.component.language.springboot.LanguageComponentAutoConfiguration.class
,org.apache.camel.component.log.springboot.LogComponentAutoConfiguration.class
The camel packages are using RelaxedPropertyResolver which is no more present in springboot 2.0
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.bind.RelaxedPropertyResolver
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
Comment From: philwebb
@espre05 This issue is closed and we prefer not to use the tracker for questions. Please ask things like this on stackoverflow.com or join us on gitter.
Comment From: voronaam
This is still an issue with Spring Boot Autoconfiguration.
There should be a way to disable a built-in autoconfiguration and provide more logic in there.
Real use case: we use a secret store and need to use its APIs to get the RSA public key (JWT token verifier key). We need to change the implementation (that can only read the token value from an URL) and we have no way to change the default implementation.
This should be considered an enhancement request, not a question.
Comment From: snicoll
There should be a way to disable a built-in autoconfiguration and provide more logic in there.
Nobody disagree with that and @philwebb already answered that concern in a comment above:
I'd rather take each need on a case-by-case basis and work out why a specific inner-configuration couldn't be used.
Please share your use case in a separate issue. A small sample that illustrates what you are doing right now is preferable to help us locate the problem.
This should be considered an enhancement request, not a question.
This wasn't a question, this was a request that got declined, see this comment above and the status: declined label on the right.
Comment From: d3minem
@snicoll and @dsyer: I was having the similar problem with configuration exclusion. A simple unit test for PasswordEncoder doesn't require to load the EmbeddedMongoDb in the spring context. @EnableAutoConfiguration exclude the inner configurations and not load embeddedMongoDb, but then it throw the error that these are not auto configurations. Is it default forced behaviour?
My unit test works, but it loads the embeddedMongoDb in the spring context which scores a penalty on performance.
EnableAutoConfiguration Error
java.lang.IllegalStateException: The following classes could not be excluded because they are not auto-configuration classes:
- com.<enclosedForTheSakeOfConfidentiality>.<handler>.config.TestMongoConfig
Spring Context
java.lang.IllegalStateException: Failed to load ApplicationContext
Suggestion
There should be a new annotation @ExcludeInnerConfiguration or @DisableInnerConfiguration - which will similarly take the placeholder classes and exclude them from the spring context.
@ExcludeInnerConfiguration(classes = {
MongoConfig.class,
TestMongoConfig.class
})
OR
@DisableInnerConfiguration(exclude = {
MongoConfig.class,
TestMongoConfig.class
})
The sole responsibility of exclusion would be the developer and he/she knows the intentions behind why? and @philwebb don't need to check case-by-case why it was discarded or even required/needed.
Comment From: artemptushkin
@philwebb @snicoll this issue is closed but it has a reasonable proposal from @d3minem up above, please consider
I struggle right now to exclude and inner config class in Kotlin tests
Comment From: snicoll
this issue is closed but it has a reasonable proposal from @d3minem
I am sorry to say that I don't think this is reasonable. An inner configuration of an auto-configuration is a internal detail of what the auto-configuration provides. If you think you have to exclude it, it might be because a condition or some sort of customization is missing and/or the test should be structured differently. We'd prefer to deal with those on a case-by-base basis as @philwebb said above already.
Comment From: artemptushkin
@snicoll if it's internal details of the parent auto-configuration then why is it scanned by default by others?
Having:
package com.example;
@Configuration
@ComponentScan("com.example")
public class App {
}
package com.example.tests;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = App.class)
public class TestWithConfig {
@Autowired
ApplicationContext applicationContext;
@Test
void with() {
assertThat(applicationContext.getBean(TestConfig.class)).isNotNull();
}
@TestConfiguration
static class TestConfig {
@Bean
public String aa() {
return "aa";
}
}
}
package com.example.tests;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = App.class)
public class TestWithoutConfig {
@Autowired
ApplicationContext applicationContext;
@Test
void itStartsWithoutABean() {
assertThatThrownBy(() -> applicationContext.getBean(TestConfig.class))
.isInstanceOf(NoSuchBeanDefinitionException.class);
assertThatThrownBy(() -> applicationContext.getBean("aa"))
.isInstanceOf(NoSuchBeanDefinitionException.class);
}
}
TestWithoutConfig fails and shows that it's scanned though it's internal and the parent is just a test class - not bean
This basically grabs any internal configuration into other contexts where it's not expected... and read App as spring boot application that scans the current package by default
upd: stackoverflow question https://stackoverflow.com/questions/72968655/whats-the-best-way-to-exclude-transitive-bean-configuration-from-scanning-witho
Comment From: snicoll
None of the code that you've shown is an auto-configuration so your problem is different entirely. Please ask on StackOverflow or come chat with us on Gitter.