Skip to content

How can I exclude the configuration enabled through a Boot protected inner configuration class? #5427

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
nucatus opened this issue Mar 16, 2016 · 15 comments
Labels
status: declined A suggestion or change that we don't feel we should currently apply

Comments

@nucatus
Copy link

nucatus commented Mar 16, 2016

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')
}
@snicoll
Copy link
Member

snicoll commented Mar 16, 2016

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.

@dsyer
Copy link
Member

dsyer commented Mar 16, 2016

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.

@nucatus
Copy link
Author

nucatus commented Mar 16, 2016

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.

@dfernandezm
Copy link

+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.

@philwebb
Copy link
Member

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.

@wilkinsona wilkinsona added the status: waiting-for-triage An issue we've not yet triaged label Jan 15, 2017
@philwebb philwebb added status: declined A suggestion or change that we don't feel we should currently apply and removed status: waiting-for-triage An issue we've not yet triaged labels Apr 27, 2017
@philwebb
Copy link
Member

We're not keen to increase the complexity of excludes. The inner configs are really meant as an implementation detail.

@espre05
Copy link

espre05 commented Apr 1, 2018

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)

@philwebb
Copy link
Member

philwebb commented Apr 2, 2018

@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.

@voronaam
Copy link

voronaam commented Jan 3, 2019

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.

@snicoll
Copy link
Member

snicoll commented Jan 3, 2019

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.

@d3minem
Copy link

d3minem commented Apr 22, 2020

@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.

@artemptushkin
Copy link

@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

@snicoll
Copy link
Member

snicoll commented Jul 13, 2022

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.

@artemptushkin

This comment was marked as off-topic.

@snicoll

This comment was marked as resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: declined A suggestion or change that we don't feel we should currently apply
Projects
None yet
Development

No branches or pull requests

10 participants