1616
1717package org .springframework .security .config .annotation .authentication .configuration ;
1818
19+ import java .util .ArrayList ;
20+ import java .util .List ;
21+
22+ import org .apache .commons .logging .Log ;
23+ import org .apache .commons .logging .LogFactory ;
24+
1925import org .springframework .context .ApplicationContext ;
2026import org .springframework .core .Ordered ;
2127import org .springframework .core .annotation .Order ;
28+ import org .springframework .core .log .LogMessage ;
2229import org .springframework .security .authentication .dao .DaoAuthenticationProvider ;
2330import org .springframework .security .authentication .password .CompromisedPasswordChecker ;
2431import org .springframework .security .config .annotation .authentication .builders .AuthenticationManagerBuilder ;
@@ -55,15 +62,35 @@ public void init(AuthenticationManagerBuilder auth) throws Exception {
5562
5663 class InitializeUserDetailsManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {
5764
65+ private final Log logger = LogFactory .getLog (getClass ());
66+
5867 @ Override
5968 public void configure (AuthenticationManagerBuilder auth ) throws Exception {
69+ List <BeanWithName <UserDetailsService >> userDetailsServices = getBeansWithName (UserDetailsService .class );
6070 if (auth .isConfigured ()) {
71+ if (!userDetailsServices .isEmpty ()) {
72+ this .logger .warn ("Global AuthenticationManager configured with an AuthenticationProvider bean. "
73+ + "UserDetailsService beans will not be used for username/password login. "
74+ + "Consider removing the AuthenticationProvider bean. "
75+ + "Alternatively, consider using the UserDetailsService in a manually instantiated "
76+ + "DaoAuthenticationProvider." );
77+ }
78+ return ;
79+ }
80+
81+ if (userDetailsServices .isEmpty ()) {
6182 return ;
6283 }
63- UserDetailsService userDetailsService = getBeanOrNull (UserDetailsService .class );
64- if (userDetailsService == null ) {
84+ else if (userDetailsServices .size () > 1 ) {
85+ List <String > beanNames = userDetailsServices .stream ().map (BeanWithName ::getName ).toList ();
86+ this .logger .warn (LogMessage .format ("Found %s UserDetailsService beans, with names %s. "
87+ + "Global Authentication Manager will not use a UserDetailsService for username/password login. "
88+ + "Consider publishing a single UserDetailsService bean." , userDetailsServices .size (),
89+ beanNames ));
6590 return ;
6691 }
92+ var userDetailsService = userDetailsServices .get (0 ).getBean ();
93+ var userDetailsServiceBeanName = userDetailsServices .get (0 ).getName ();
6794 PasswordEncoder passwordEncoder = getBeanOrNull (PasswordEncoder .class );
6895 UserDetailsPasswordService passwordManager = getBeanOrNull (UserDetailsPasswordService .class );
6996 CompromisedPasswordChecker passwordChecker = getBeanOrNull (CompromisedPasswordChecker .class );
@@ -83,6 +110,9 @@ public void configure(AuthenticationManagerBuilder auth) throws Exception {
83110 }
84111 provider .afterPropertiesSet ();
85112 auth .authenticationProvider (provider );
113+ this .logger .info (LogMessage .format (
114+ "Global AuthenticationManager configured with UserDetailsService bean with name %s" ,
115+ userDetailsServiceBeanName ));
86116 }
87117
88118 /**
@@ -97,6 +127,41 @@ private <T> T getBeanOrNull(Class<T> type) {
97127 return InitializeUserDetailsBeanManagerConfigurer .this .context .getBean (beanNames [0 ], type );
98128 }
99129
130+ /**
131+ * @return a list of beans of the requested class, along with their names. If
132+ * there are no registered beans of that type, the list is empty.
133+ */
134+ private <T > List <BeanWithName <T >> getBeansWithName (Class <T > type ) {
135+ List <BeanWithName <T >> beanWithNames = new ArrayList <>();
136+ String [] beanNames = InitializeUserDetailsBeanManagerConfigurer .this .context .getBeanNamesForType (type );
137+ for (String beanName : beanNames ) {
138+ T bean = InitializeUserDetailsBeanManagerConfigurer .this .context .getBean (beanNames [0 ], type );
139+ beanWithNames .add (new BeanWithName <T >(bean , beanName ));
140+ }
141+ return beanWithNames ;
142+ }
143+
144+ static class BeanWithName <T > {
145+
146+ private final T bean ;
147+
148+ private final String name ;
149+
150+ BeanWithName (T bean , String name ) {
151+ this .bean = bean ;
152+ this .name = name ;
153+ }
154+
155+ T getBean () {
156+ return this .bean ;
157+ }
158+
159+ String getName () {
160+ return this .name ;
161+ }
162+
163+ }
164+
100165 }
101166
102167}
0 commit comments