1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.springframework.security.providers;
17
18 import org.springframework.security.AbstractAuthenticationManager;
19 import org.springframework.security.AccountExpiredException;
20 import org.springframework.security.SpringSecurityMessageSource;
21 import org.springframework.security.Authentication;
22 import org.springframework.security.AuthenticationException;
23 import org.springframework.security.AuthenticationServiceException;
24 import org.springframework.security.BadCredentialsException;
25 import org.springframework.security.CredentialsExpiredException;
26 import org.springframework.security.DisabledException;
27 import org.springframework.security.LockedException;
28 import org.springframework.security.AccountStatusException;
29 import org.springframework.security.concurrent.ConcurrentLoginException;
30 import org.springframework.security.concurrent.ConcurrentSessionController;
31 import org.springframework.security.concurrent.NullConcurrentSessionController;
32 import org.springframework.security.event.authentication.AbstractAuthenticationEvent;
33 import org.springframework.security.event.authentication.AuthenticationFailureBadCredentialsEvent;
34 import org.springframework.security.event.authentication.AuthenticationFailureConcurrentLoginEvent;
35 import org.springframework.security.event.authentication.AuthenticationFailureCredentialsExpiredEvent;
36 import org.springframework.security.event.authentication.AuthenticationFailureDisabledEvent;
37 import org.springframework.security.event.authentication.AuthenticationFailureExpiredEvent;
38 import org.springframework.security.event.authentication.AuthenticationFailureLockedEvent;
39 import org.springframework.security.event.authentication.AuthenticationFailureProviderNotFoundEvent;
40 import org.springframework.security.event.authentication.AuthenticationFailureProxyUntrustedEvent;
41 import org.springframework.security.event.authentication.AuthenticationFailureServiceExceptionEvent;
42 import org.springframework.security.event.authentication.AuthenticationSuccessEvent;
43 import org.springframework.security.userdetails.UsernameNotFoundException;
44
45 import org.springframework.beans.factory.InitializingBean;
46 import org.springframework.context.ApplicationEvent;
47 import org.springframework.context.ApplicationEventPublisher;
48 import org.springframework.context.ApplicationEventPublisherAware;
49 import org.springframework.context.MessageSource;
50 import org.springframework.context.MessageSourceAware;
51 import org.springframework.context.support.MessageSourceAccessor;
52 import org.springframework.util.Assert;
53
54 import org.apache.commons.logging.Log;
55 import org.apache.commons.logging.LogFactory;
56
57 import java.lang.reflect.Constructor;
58 import java.lang.reflect.InvocationTargetException;
59
60 import java.util.Iterator;
61 import java.util.List;
62 import java.util.Properties;
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 public class ProviderManager extends AbstractAuthenticationManager implements InitializingBean, MessageSourceAware,
102 ApplicationEventPublisherAware {
103
104
105 private static final Log logger = LogFactory.getLog(ProviderManager.class);
106 private static final Properties DEFAULT_EXCEPTION_MAPPINGS = new Properties();
107
108
109
110 private ApplicationEventPublisher applicationEventPublisher;
111 private ConcurrentSessionController sessionController = new NullConcurrentSessionController();
112 private List providers;
113 protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
114 private Properties exceptionMappings = new Properties();
115 private Properties additionalExceptionMappings = new Properties();
116
117 static {
118 DEFAULT_EXCEPTION_MAPPINGS.put(AccountExpiredException.class.getName(),
119 AuthenticationFailureExpiredEvent.class.getName());
120 DEFAULT_EXCEPTION_MAPPINGS.put(AuthenticationServiceException.class.getName(),
121 AuthenticationFailureServiceExceptionEvent.class.getName());
122 DEFAULT_EXCEPTION_MAPPINGS.put(LockedException.class.getName(),
123 AuthenticationFailureLockedEvent.class.getName());
124 DEFAULT_EXCEPTION_MAPPINGS.put(CredentialsExpiredException.class.getName(),
125 AuthenticationFailureCredentialsExpiredEvent.class.getName());
126 DEFAULT_EXCEPTION_MAPPINGS.put(DisabledException.class.getName(),
127 AuthenticationFailureDisabledEvent.class.getName());
128 DEFAULT_EXCEPTION_MAPPINGS.put(BadCredentialsException.class.getName(),
129 AuthenticationFailureBadCredentialsEvent.class.getName());
130 DEFAULT_EXCEPTION_MAPPINGS.put(UsernameNotFoundException.class.getName(),
131 AuthenticationFailureBadCredentialsEvent.class.getName());
132 DEFAULT_EXCEPTION_MAPPINGS.put(ConcurrentLoginException.class.getName(),
133 AuthenticationFailureConcurrentLoginEvent.class.getName());
134 DEFAULT_EXCEPTION_MAPPINGS.put(ProviderNotFoundException.class.getName(),
135 AuthenticationFailureProviderNotFoundEvent.class.getName());
136 DEFAULT_EXCEPTION_MAPPINGS.put("org.springframework.security.providers.cas.ProxyUntrustedException",
137 AuthenticationFailureProxyUntrustedEvent.class.getName());
138 }
139
140 public ProviderManager() {
141 exceptionMappings.putAll(DEFAULT_EXCEPTION_MAPPINGS);
142 }
143
144
145
146 public void afterPropertiesSet() throws Exception {
147 Assert.notNull(this.messages, "A message source must be set");
148 exceptionMappings.putAll(additionalExceptionMappings);
149 }
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169 public Authentication doAuthentication(Authentication authentication) throws AuthenticationException {
170 Iterator iter = getProviders().iterator();
171
172 Class toTest = authentication.getClass();
173
174 AuthenticationException lastException = null;
175
176 while (iter.hasNext()) {
177 AuthenticationProvider provider = (AuthenticationProvider) iter.next();
178
179 if (!provider.supports(toTest)) {
180 continue;
181 }
182
183 logger.debug("Authentication attempt using " + provider.getClass().getName());
184
185 Authentication result;
186
187 try {
188 result = provider.authenticate(authentication);
189
190 if (result != null) {
191 copyDetails(authentication, result);
192 sessionController.checkAuthenticationAllowed(result);
193 }
194 } catch (AuthenticationException ae) {
195 lastException = ae;
196 result = null;
197 }
198
199
200
201 if (lastException instanceof AccountStatusException || lastException instanceof ConcurrentLoginException) {
202 break;
203 }
204
205 if (result != null) {
206 sessionController.registerSuccessfulAuthentication(result);
207 publishEvent(new AuthenticationSuccessEvent(result));
208
209 return result;
210 }
211 }
212
213 if (lastException == null) {
214 lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound",
215 new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}"));
216 }
217
218 publishAuthenticationFailure(lastException, authentication);
219
220 throw lastException;
221 }
222
223 private void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) {
224 String className = exceptionMappings.getProperty(exception.getClass().getName());
225 AbstractAuthenticationEvent event = null;
226
227 if (className != null) {
228 try {
229 Class clazz = getClass().getClassLoader().loadClass(className);
230 Constructor constructor = clazz.getConstructor(new Class[] {
231 Authentication.class, AuthenticationException.class
232 });
233 Object obj = constructor.newInstance(new Object[] {authentication, exception});
234 Assert.isInstanceOf(AbstractAuthenticationEvent.class, obj, "Must be an AbstractAuthenticationEvent");
235 event = (AbstractAuthenticationEvent) obj;
236 } catch (ClassNotFoundException ignored) {}
237 catch (NoSuchMethodException ignored) {}
238 catch (IllegalAccessException ignored) {}
239 catch (InstantiationException ignored) {}
240 catch (InvocationTargetException ignored) {}
241 }
242
243 if (event != null) {
244 publishEvent(event);
245 } else {
246 if (logger.isDebugEnabled()) {
247 logger.debug("No event was found for the exception " + exception.getClass().getName());
248 }
249 }
250
251 }
252
253
254
255
256
257
258
259
260 private void copyDetails(Authentication source, Authentication dest) {
261 if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) {
262 AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest;
263
264 token.setDetails(source.getDetails());
265 }
266 }
267
268 public List getProviders() {
269 if (providers == null || providers.size() == 0) {
270 throw new IllegalArgumentException("A list of AuthenticationProviders is required");
271 }
272
273 return providers;
274 }
275
276
277
278
279
280
281
282 public ConcurrentSessionController getSessionController() {
283 return sessionController;
284 }
285
286 public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
287 this.applicationEventPublisher = applicationEventPublisher;
288 }
289
290 public void setMessageSource(MessageSource messageSource) {
291 this.messages = new MessageSourceAccessor(messageSource);
292 }
293
294
295
296
297
298
299
300
301
302 public void setProviders(List providers) {
303 Assert.notEmpty(providers, "A list of AuthenticationProviders is required");
304 Iterator iter = providers.iterator();
305
306 while (iter.hasNext()) {
307 Object currentObject = iter.next();
308 Assert.isInstanceOf(AuthenticationProvider.class, currentObject,
309 "Can only provide AuthenticationProvider instances");
310 }
311
312 this.providers = providers;
313 }
314
315
316
317
318
319
320
321 public void setSessionController(ConcurrentSessionController sessionController) {
322 this.sessionController = sessionController;
323 }
324
325 private void publishEvent(ApplicationEvent event) {
326 if (applicationEventPublisher != null) {
327 applicationEventPublisher.publishEvent(event);
328 }
329 }
330
331
332
333
334
335
336
337
338 public void setAdditionalExceptionMappings(Properties additionalExceptionMappings) {
339 this.additionalExceptionMappings = additionalExceptionMappings;
340 }
341 }