16
16
17
17
package org .springframework .security .config .annotation .authentication .configuration ;
18
18
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
+
19
25
import org .springframework .context .ApplicationContext ;
20
26
import org .springframework .core .Ordered ;
21
27
import org .springframework .core .annotation .Order ;
28
+ import org .springframework .core .log .LogMessage ;
22
29
import org .springframework .security .authentication .dao .DaoAuthenticationProvider ;
23
30
import org .springframework .security .authentication .password .CompromisedPasswordChecker ;
24
31
import org .springframework .security .config .annotation .authentication .builders .AuthenticationManagerBuilder ;
@@ -55,15 +62,35 @@ public void init(AuthenticationManagerBuilder auth) throws Exception {
55
62
56
63
class InitializeUserDetailsManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {
57
64
65
+ private final Log logger = LogFactory .getLog (getClass ());
66
+
58
67
@ Override
59
68
public void configure (AuthenticationManagerBuilder auth ) throws Exception {
69
+ List <BeanWithName <UserDetailsService >> userDetailsServices = getBeansWithName (UserDetailsService .class );
60
70
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 ()) {
61
82
return ;
62
83
}
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 ));
65
90
return ;
66
91
}
92
+ var userDetailsService = userDetailsServices .get (0 ).getBean ();
93
+ var userDetailsServiceBeanName = userDetailsServices .get (0 ).getName ();
67
94
PasswordEncoder passwordEncoder = getBeanOrNull (PasswordEncoder .class );
68
95
UserDetailsPasswordService passwordManager = getBeanOrNull (UserDetailsPasswordService .class );
69
96
CompromisedPasswordChecker passwordChecker = getBeanOrNull (CompromisedPasswordChecker .class );
@@ -83,6 +110,9 @@ public void configure(AuthenticationManagerBuilder auth) throws Exception {
83
110
}
84
111
provider .afterPropertiesSet ();
85
112
auth .authenticationProvider (provider );
113
+ this .logger .info (LogMessage .format (
114
+ "Global AuthenticationManager configured with UserDetailsService bean with name %s" ,
115
+ userDetailsServiceBeanName ));
86
116
}
87
117
88
118
/**
@@ -97,6 +127,41 @@ private <T> T getBeanOrNull(Class<T> type) {
97
127
return InitializeUserDetailsBeanManagerConfigurer .this .context .getBean (beanNames [0 ], type );
98
128
}
99
129
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
+
100
165
}
101
166
102
167
}
0 commit comments