Summary
I am currently on spring security 5.7.5 and working on upgrading to 6. When running with 6.0.0-M6 my app boots up. But once I change to 6.0.0-RC1 I start seeing an error while it is configuring and starting up. Also, I use an XML config.
Actual Behavior
I receive the following stacktrace:
Caused by: java.lang.IllegalArgumentException: Found 47 beans for type interface org.springframework.security.authentication.AuthenticationManager, but none marked as primary
2022-12-14T19:34:46.812217246Z at org.springframework.util.Assert.isTrue(Assert.java:139)
2022-12-14T19:34:46.812219595Z at org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration.getBeanName(AuthenticationConfiguration.java:174)
2022-12-14T19:34:46.812221941Z at org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration.lazyBean(AuthenticationConfiguration.java:160)
2022-12-14T19:34:46.812224389Z at org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration.getAuthenticationManagerBean(AuthenticationConfiguration.java:196)
2022-12-14T19:34:46.812229284Z at org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration.getAuthenticationManager(AuthenticationConfiguration.java:123)
2022-12-14T19:34:46.812232179Z at org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.authenticationManager(HttpSecurityConfiguration.java:132)
2022-12-14T19:34:46.812235454Z at org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity(HttpSecurityConfiguration.java:108)
2022-12-14T19:34:46.812238663Z at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
2022-12-14T19:34:46.812241062Z at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
2022-12-14T19:34:46.812243689Z at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
2022-12-14T19:34:46.812246290Z at java.base/java.lang.reflect.Method.invoke(Unknown Source)
2022-12-14T19:34:46.812248692Z at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:138)
2022-12-14T19:34:46.812251096Z ... 69 common frames omitted
If I slowly remove some of my XML config, I get less and less beans in that error message. For instance if my XML is just
<security:http pattern="/customers/**" auto-config="true" use-expressions="true" servlet-api-provision="true">
<security:intercept-url pattern="/customers/login" access="permitAll" />
<security:intercept-url pattern="/customers/**" access="hasRole('ROLE_FULLY_AUTH')" />
</security:http>
Then I get this error:
Caused by: java.lang.IllegalArgumentException: Found 3 beans for type interface org.springframework.security.authentication.AuthenticationManager, but none marked as primary
If I remove that security:http block all together, it starts, but I obviously don't have any of paths secured.
Expected Behavior
The app starts successfully.
Configuration
XML Spring security.
Auth manager defined in the XML
<security:authentication-manager>
<security:authentication-provider ref="customAuthenticationProvider" />
</security:authentication-manager>
Version
6.0.0-RC1 or newer
Comment From: vandykej17
By adding an alias on my auth manager
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="customAuthenticationProvider" />
</security:authentication-manager>
And referencing that in my security settings, I have reduced the error to 2 beans.
authentication-manager-ref="authenticationManager"
Caused by: java.lang.IllegalArgumentException: Found 2 beans for type interface org.springframework.security.authentication.AuthenticationManager, but none marked as primary
Comment From: marcusdacoregio
Hi @vandykej17, thanks for the report.
Can you share what your configuration that is using the custom AuthenticationManager looks like? Or, if possible, provide a minimal, reproducible sample that we can just clone and run.
Comment From: vandykej17
Unfortunately, it will be difficult for me to separate this large app into a minimal reproducible sample.
After researching more, it looks like each security:http entry generates 2 auth managers. If I set authentication-manager-ref="authenticationManager" it generates only 1 here - https://github.com/spring-projects/spring-security/blob/main/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java#L152
After realizing this I implemented the following and am up and running again.
@Configuration
public class CustomBeanProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Arrays.stream(beanFactory.getBeanNamesForType(AuthenticationManager.class)).filter(beanName -> beanName.equals("org.springframework.security.authenticationManager"))
.findFirst().ifPresent(beanName -> beanFactory.getBeanDefinition(beanName).setPrimary(true));
}
}
Although I don't love this solution, it is working.
Comment From: marcusdacoregio
I tried a few ways and unfortunately, I was not able to reproduce this problem. It would be great if you could share your XML configuration
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: spring-projects-issues
Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.
Comment From: antenia-lhubert
Hi, I encountered the same issue on a project, any eta on this for xml configurations?
Comment From: BeardOverflow
Hi @antenia-lhubert If you want to revert the legacy behaviour with no primary AuthenticationManager bean constraint for HttpSecurity context, try the following snippet:
@Configuration
public class NullAuthenticationManagerConfiguration {
@Bean
public AuthenticationManager nullAuthenticationManager() {
return new ProviderManager(new NullAuthenticationProvider());
}
@Lazy
@Autowired
public void configure(
final AuthenticationManagerBuilder builder,
final AuthenticationManager nullAuthenticationManager
) {
builder.parentAuthenticationManager(nullAuthenticationManager);
}
}
In few words, with the above snippet, HttpSecurity context refers to a dummy AuthenticationManager which always goes to error 401 instead of a primary AuthenticationManager. Now remember to configure your SecurityFilterChain(s) with an AuthenticationManager fits your needs.
@Configuration
public class SecurityConfiguration {
@Bean
public SecurityFilterChain mySecurityFilterChain(final HttpSecurity http) throws Exception {
return http.authenticationManager(myAuthenticationManagerWhichIsNotMarkedAsPrimaryBecauseOfComplexReasons).build();
}
}
My answer is based on: https://github.com/spring-projects/spring-security/blob/bc8ba7f3b7fa03346337fe99177da60aaaa96c34/docs/modules/ROOT/pages/servlet/authentication/passwords/index.adoc#customize-the-authenticationmanager
For digging deeper, see HttpSecurityConfiguration.java and AuthenticationConfiguration.java files: - https://github.com/spring-projects/spring-security/blob/bc8ba7f3b7fa03346337fe99177da60aaaa96c34/config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java#L140-L142 - https://github.com/spring-projects/spring-security/blob/bc8ba7f3b7fa03346337fe99177da60aaaa96c34/config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java#L114
Comment From: vandykej17
For what it's worth, when we upgraded, we converted our XML security config to Java config and did not see the bug again. Not sure if that was a purposeful decision to not support the XML config or not, but that was the easier route for us. Plus, it made it more readable and modern.
Comment From: antenia-lhubert
Thanks a lot for your answers, but I am working on a legacy project and converting the xml based configuration to Java would be quite complex.
By messing around I have found a solution: Instead of declaring the authentication manager with spring security tags, I do it with beans and set primary to true
<!-- before -->
<security:authentication-manager id="myAuthenticationManager">
<security:authentication-provider ref="customAuthenticationProvider" />
</security:authentication-manager>
<!-- after -->
<bean id="myAuthenticationManager" class="org.springframework.security.authentication.ProviderManager" primary="true">
<constructor-arg>
<list>
<ref bean="customAuthenticationProvider"/>
</list>
</constructor-arg>
</bean>