Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Starter autoconfig bean always takes precedence over custom autoconfig bean

I'm trying to make a custom spring boot starter that will be used by multiple projects to authenticate with Azure AD. All the Azure AD config has been set up and an individual project hardcoded with all the settings to work with Azure AD works fine too. Now I'm trying to move these settings into a custom Spring Boot starter so that multiple projects can use it. It works for the most part, except for one thing: moving the bean config for a custom AADAppRoleStatelessAuthenticationFilter. If I leave my custom implementation (CustomAADAppRoleStatelessAuthFilter) hardcoded in the actual implementing project, everything works and only CustomAADAppRoleStatelessAuthFilter is created, but as soon as I move it into the custom starter, I only ever get AADAppRoleStatelessAuthenticationFilter instead.

Note that my CustomAADAppRoleStatelessAuthFilter extends the starter's AADAppRoleStatelessAuthenticationFilter.

The autoconfig for AADAppRoleStatelessAuthenticationFilter in the azure-spring-boot project (https://github.com/microsoft/azure-spring-boot/blob/master/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/AADAuthenticationFilterAutoConfiguration.java) is:

@Bean
@ConditionalOnMissingBean(AADAppRoleStatelessAuthenticationFilter.class)
@ConditionalOnProperty(prefix = PROPERTY_PREFIX, value = PROPERTY_SESSION_STATELESS, havingValue = "true")
public AADAppRoleStatelessAuthenticationFilter azureADStatelessAuthFilter(ResourceRetriever resourceRetriever) {
    //bean details omitted
}

My custom autoconfig that should replace the above is as follows:

@Bean
@ConditionalOnMissingBean(AADAppRoleStatelessAuthenticationFilter.class)
@ConditionalOnProperty(prefix = PROPERTY_PREFIX, value = PROPERTY_SESSION_STATELESS, havingValue = "true")    
public AADAppRoleStatelessAuthenticationFilter customAADAppRoleStatelessAuthFilter(
        ResourceRetriever resourceRetriever) {
    return new CustomAADAppRoleStatelessAuthenticationFilter(/*details omitted*/);
}

No amount of @AutoConfigureBefore(AADAuthenticationFilterAutoConfiguration.class) works.

Also, if I change the condition on my custom bean to be the subtype (@ConditionalOnMissingBean(CustomAADAppRoleStatelessAuthFilter.class)), BOTH types get created, and I can autowire my CustomAwareAADAppRoleStatelessAuthFilter and put it in my WebSecurityConfigurerAdapter, but things STILL won't work. I debugged things and found that the CustomAADAppRoleStatelessAuthFilter is the only bean of the ADAppRoleStatelessAuthenticationFilter type in my spring security filter chain, but that once the 'end of the additional filter chain' has completed and the 'original chain proceeds', I find that the ADAppRoleStatelessAuthenticationFilter has fired! And of course it throws an error because my CustomAADAppRoleStatelessAuthFilter has already done things to customize the UserPrincipal. I can't figure out where the ADAppRoleStatelessAuthenticationFilter is getting added to any filter chain, and even if I mark my CustomAADAppRoleStatelessAuthFilter bean with @Primary, the starter ADAppRoleStatelessAuthenticationFilter will still be used instead.

The only 'solutions' that have worked are to define the CustomAADAppRoleStatelessAuthFilter in the actual implementing project instead of the custom starter project, or to exclude the AADAuthenticationFilterAutoConfiguration in my actual implementing project's @SpringBootApplication annotation (Not even excluding it the property-based way works).

Is there a way to make AADAuthenticationFilterAutoConfigurations ADAppRoleStatelessAuthenticationFilter bean definition back off? 'Cause @AutoConfigureBefore(AADAuthenticationFilterAutoConfiguration.class) on my custom auto configuration class that has my CustomAADAppRoleStatelessAuthFilter definition doesn't work, and having all the implementing projects explicitly exclude AADAuthenticationFilterAutoConfiguration isn't the most ideal solution (although at least with that solution they don't all need to declare their own bean definition for CustomAADAppRoleStatelessAuthFilter).

like image 250
AForsberg Avatar asked Oct 16 '22 07:10

AForsberg


People also ask

What is the difference between @configuration and AutoConfiguration?

AutoConfiguration classes are run last (meaning after all regular non-autoconfiguration classes) while the order in which Configuration classes are run is indeterminate (except if we use ordering annotations like @Ordered ) To declare a class as an AutoConfiguration they need to be specified as such in the spring.

For which of the following criteria can spring boot auto-configuration be done?

For example, if the H2 database Jar is present in the classpath and we have not configured any beans related to the database manually, the Spring Boot's auto-configuration feature automatically configures it in the project. We can enable the auto-configuration feature by using the annotation @EnableAutoConfiguration.

How does AutoConfiguration work in spring boot?

Spring Boot auto-configuration attempts to automatically configure your Spring application based on the jar dependencies that you have added. For example, If HSQLDB is on your classpath, and you have not manually configured any database connection beans, then we will auto-configure an in-memory database.

Can we have conditional based presence or absence of specific bean in spring boot?

The @ConditionalOnBean and @ConditionalOnMissingBean annotations allow configurations to be skipped based on the presence or absence of specific beans. You can use the value attribute to specify beans by type, or name to specify beans by name.


2 Answers

Have you tried using @Order and assign your custom bean a higer precedence. By default all the beans gets lowest priority (Ordered.LOWEST_PRECEDENCE) losing to any other specified order value.

@Order(Ordered.LOWEST_PRECEDENCE - 1)
@Bean
@ConditionalOnMissingBean(AADAppRoleStatelessAuthenticationFilter.class)
@ConditionalOnProperty(prefix = PROPERTY_PREFIX, value = PROPERTY_SESSION_STATELESS, havingValue = "true")    
public AADAppRoleStatelessAuthenticationFilter customAADAppRoleStatelessAuthFilter(
    ResourceRetriever resourceRetriever) {
    return new CustomAADAppRoleStatelessAuthenticationFilter(/*details omitted*/);
}

Can you try putting the @Order(Ordered.LOWEST_PRECEDENCE - 1) like i mentioned above? Your bean should then take precedence over the other.

like image 164
Akhil Bojedla Avatar answered Oct 19 '22 00:10

Akhil Bojedla


Have you tried adding @Primary to your bean?

like image 26
Gandalf Avatar answered Oct 19 '22 01:10

Gandalf