Honor @Primary for UserDetailsService and UserDetailsPasswordService in InitializeUserDetailsBeanManagerConfigurer#18015
Honor @Primary for UserDetailsService and UserDetailsPasswordService in InitializeUserDetailsBeanManagerConfigurer#18015siva-sai-udaygiri wants to merge 0 commit into
Conversation
jzheaux
left a comment
There was a problem hiding this comment.
Thanks, @siva-sai-udaygirl, for the PR! I've left some feedback inline.
|
|
||
| PasswordEncoder passwordEncoder = getBeanIfUnique(PasswordEncoder.class); | ||
| // Also resolve UDPS via container so @Primary is honored | ||
| UserDetailsPasswordService passwordManager = getAutowireCandidateOrNull(UserDetailsPasswordService.class); |
There was a problem hiding this comment.
I believe this changes the existing semantics by calling getIfAvailable instead of getIfUnique. The ObjectProvider JavaDoc implies that it accounts for @Primary:
/**
* Return an instance (possibly shared or independent) of the object
* managed by this factory.
* @return an instance of the bean, or {@code null} if not available or
* not unique (i.e. multiple candidates found **with none marked as primary**)
* @throws BeansException in case of creation errors
* @see #getObject()
*/Can you confirm with unit tests that changing to getIfAvailable is necessary to support @Primary?
There was a problem hiding this comment.
Thanks, @jzheaux! I added unit tests to show why we need to rely on the container’s autowire-candidate resolution to honor @primary.
• With getIfUnique(...) when there are two UserDetailsService beans and one is @primary, it still returns null (multiple names), so the global AuthenticationManager isn’t configured.
• With getIfAvailable(...) it delegates to autowire-candidate selection (same as normal injection), so the @primary bean is chosen, and the AuthenticationManager is configured.
New tests:
- whenMultipleUdsAndOneResolvableCandidate_thenPrimaryIsAutoWired
- whenMultipleUdsAndNoSingleCandidate_thenSkipAutoWiring
Location: spring-security-config/src/test/java/org/springframework/security/config/annotation/authentication/configuration/InitializeUserDetailsBeanManagerConfigurerTests.java
This keeps behavior the same for the single-bean case, and for multiple beans without a single resolvable candidate we still skip auto-wiring (and log). Please take another look and let me know if you’d prefer a different approach.
Motivation
In scenarios where multiple
UserDetailsServiceorUserDetailsPasswordServicebeansare defined, Spring may raise
NoUniqueBeanDefinitionExceptionor select the wrongbean without explicit resolution.
Changes
@PrimarytoUserDetailsServiceandUserDetailsPasswordServicebeans inInitializeUserDetailsBeanManagerConfigurerIssue Reference
Fixes gh-17902
Testing
InitializeUserDetailsBeanManagerConfigurerTeststo validateresolution when multiple candidates are present
./gradlew checkNotes