1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.springframework.security.ui;
17
18 import org.springframework.security.SpringSecurityMessageSource;
19 import org.springframework.security.Authentication;
20 import org.springframework.security.AuthenticationException;
21 import org.springframework.security.AuthenticationManager;
22 import org.springframework.security.util.RedirectUtils;
23 import org.springframework.security.util.SessionUtils;
24 import org.springframework.security.util.UrlUtils;
25
26 import org.springframework.security.concurrent.SessionRegistry;
27 import org.springframework.security.context.SecurityContextHolder;
28
29 import org.springframework.security.event.authentication.InteractiveAuthenticationSuccessEvent;
30
31 import org.springframework.security.ui.rememberme.NullRememberMeServices;
32 import org.springframework.security.ui.rememberme.RememberMeServices;
33 import org.springframework.security.ui.savedrequest.SavedRequest;
34
35 import org.springframework.beans.factory.InitializingBean;
36
37 import org.springframework.context.ApplicationEventPublisher;
38 import org.springframework.context.ApplicationEventPublisherAware;
39 import org.springframework.context.MessageSource;
40 import org.springframework.context.MessageSourceAware;
41 import org.springframework.context.support.MessageSourceAccessor;
42
43 import org.springframework.util.Assert;
44
45 import java.io.IOException;
46
47 import java.util.Properties;
48
49 import javax.servlet.FilterChain;
50 import javax.servlet.ServletException;
51 import javax.servlet.http.HttpServletRequest;
52 import javax.servlet.http.HttpServletResponse;
53 import javax.servlet.http.HttpSession;
54
55
56
57
58
59
60
61
62
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130 public abstract class AbstractProcessingFilter extends SpringSecurityFilter implements InitializingBean,
131 ApplicationEventPublisherAware, MessageSourceAware {
132
133
134 public static final String SPRING_SECURITY_SAVED_REQUEST_KEY = "SPRING_SECURITY_SAVED_REQUEST_KEY";
135
136 public static final String SPRING_SECURITY_LAST_EXCEPTION_KEY = "SPRING_SECURITY_LAST_EXCEPTION";
137
138
139
140 protected ApplicationEventPublisher eventPublisher;
141
142 protected AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
143
144 private AuthenticationManager authenticationManager;
145
146 protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
147
148 private Properties exceptionMappings = new Properties();
149
150
151
152
153
154 private RememberMeServices rememberMeServices = null;
155
156 private TargetUrlResolver targetUrlResolver = new TargetUrlResolverImpl();
157
158
159 private String authenticationFailureUrl;
160
161
162
163
164
165 private String defaultTargetUrl;
166
167
168
169
170
171 private String filterProcessesUrl = getDefaultFilterProcessesUrl();
172
173
174
175
176
177
178
179 private boolean alwaysUseDefaultTargetUrl = false;
180
181
182
183
184
185
186
187 private boolean continueChainBeforeSuccessfulAuthentication = false;
188
189
190
191
192
193 private boolean useRelativeContext = false;
194
195
196
197
198
199
200
201 private boolean invalidateSessionOnSuccessfulAuthentication = false;
202
203
204
205
206
207
208
209
210 private boolean migrateInvalidatedSessionAttributes = true;
211
212 private boolean allowSessionCreation = true;
213
214 private boolean serverSideRedirect = false;
215
216 private SessionRegistry sessionRegistry;
217
218
219
220 public void afterPropertiesSet() throws Exception {
221 Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
222 Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL");
223 Assert.hasLength(defaultTargetUrl, "defaultTargetUrl must be specified");
224 Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultTargetUrl), defaultTargetUrl + " isn't a valid redirect URL");
225 Assert.isTrue(UrlUtils.isValidRedirectUrl(authenticationFailureUrl), authenticationFailureUrl + " isn't a valid redirect URL");
226 Assert.notNull(authenticationManager, "authenticationManager must be specified");
227 Assert.notNull(targetUrlResolver, "targetUrlResolver cannot be null");
228
229 if (rememberMeServices == null) {
230 rememberMeServices = new NullRememberMeServices();
231 }
232 }
233
234
235
236
237
238
239
240
241
242
243
244 public abstract Authentication attemptAuthentication(HttpServletRequest request) throws AuthenticationException;
245
246 public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException,
247 ServletException {
248
249 if (requiresAuthentication(request, response)) {
250 if (logger.isDebugEnabled()) {
251 logger.debug("Request is to process authentication");
252 }
253
254 Authentication authResult;
255
256 try {
257 onPreAuthentication(request, response);
258 authResult = attemptAuthentication(request);
259 }
260 catch (AuthenticationException failed) {
261
262 unsuccessfulAuthentication(request, response, failed);
263
264 return;
265 }
266
267
268 if (continueChainBeforeSuccessfulAuthentication) {
269 chain.doFilter(request, response);
270 }
271
272 successfulAuthentication(request, response, authResult);
273
274 return;
275 }
276
277 chain.doFilter(request, response);
278 }
279
280 public static String obtainFullSavedRequestUrl(HttpServletRequest request) {
281 SavedRequest savedRequest = getSavedRequest(request);
282
283 return savedRequest == null ? null : savedRequest.getFullRequestUrl();
284 }
285
286 private static SavedRequest getSavedRequest(HttpServletRequest request) {
287 HttpSession session = request.getSession(false);
288
289 if (session == null) {
290 return null;
291 }
292
293 SavedRequest savedRequest = (SavedRequest) session.getAttribute(SPRING_SECURITY_SAVED_REQUEST_KEY);
294
295 return savedRequest;
296 }
297
298 protected void onPreAuthentication(HttpServletRequest request, HttpServletResponse response)
299 throws AuthenticationException, IOException {
300 }
301
302 protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
303 Authentication authResult) throws IOException {
304 }
305
306 protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
307 AuthenticationException failed) throws IOException {
308 }
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332 protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
333 String uri = request.getRequestURI();
334 int pathParamIndex = uri.indexOf(';');
335
336 if (pathParamIndex > 0) {
337
338 uri = uri.substring(0, pathParamIndex);
339 }
340
341 if ("".equals(request.getContextPath())) {
342 return uri.endsWith(filterProcessesUrl);
343 }
344
345 return uri.endsWith(request.getContextPath() + filterProcessesUrl);
346 }
347
348 protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
349 throws IOException {
350
351 RedirectUtils.sendRedirect(request, response, url, useRelativeContext);
352 }
353
354 protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
355 Authentication authResult) throws IOException, ServletException {
356 if (logger.isDebugEnabled()) {
357 logger.debug("Authentication success: " + authResult.toString());
358 }
359
360 SecurityContextHolder.getContext().setAuthentication(authResult);
361
362 if (logger.isDebugEnabled()) {
363 logger.debug("Updated SecurityContextHolder to contain the following Authentication: '" + authResult + "'");
364 }
365
366 if (invalidateSessionOnSuccessfulAuthentication) {
367 SessionUtils.startNewSessionIfRequired(request, migrateInvalidatedSessionAttributes, sessionRegistry);
368 }
369
370 String targetUrl = determineTargetUrl(request);
371
372 if (logger.isDebugEnabled()) {
373 logger.debug("Redirecting to target URL from HTTP Session (or default): " + targetUrl);
374 }
375
376 onSuccessfulAuthentication(request, response, authResult);
377
378 rememberMeServices.loginSuccess(request, response, authResult);
379
380
381 if (this.eventPublisher != null) {
382 eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
383 }
384
385 sendRedirect(request, response, targetUrl);
386 }
387
388 protected String determineTargetUrl(HttpServletRequest request) {
389
390 String targetUrl = alwaysUseDefaultTargetUrl ? null :
391 targetUrlResolver.determineTargetUrl(getSavedRequest(request), request, SecurityContextHolder.getContext().getAuthentication());
392
393 if (targetUrl == null) {
394 targetUrl = getDefaultTargetUrl();
395 }
396
397 return targetUrl;
398 }
399
400 protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
401 AuthenticationException failed) throws IOException, ServletException {
402 SecurityContextHolder.getContext().setAuthentication(null);
403
404 if (logger.isDebugEnabled()) {
405 logger.debug("Updated SecurityContextHolder to contain null Authentication");
406 }
407
408 String failureUrl = determineFailureUrl(request, failed);
409
410 if (logger.isDebugEnabled()) {
411 logger.debug("Authentication request failed: " + failed.toString());
412 }
413
414 try {
415 HttpSession session = request.getSession(false);
416
417 if (session != null || allowSessionCreation) {
418 request.getSession().setAttribute(SPRING_SECURITY_LAST_EXCEPTION_KEY, failed);
419 }
420 }
421 catch (Exception ignored) {
422 }
423
424 onUnsuccessfulAuthentication(request, response, failed);
425
426 rememberMeServices.loginFail(request, response);
427
428 if (failureUrl == null) {
429 response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed:" + failed.getMessage());
430 } else if (serverSideRedirect){
431 request.getRequestDispatcher(failureUrl).forward(request, response);
432 } else {
433 sendRedirect(request, response, failureUrl);
434 }
435 }
436
437 protected String determineFailureUrl(HttpServletRequest request, AuthenticationException failed) {
438 return exceptionMappings.getProperty(failed.getClass().getName(), authenticationFailureUrl);
439 }
440
441 public String getAuthenticationFailureUrl() {
442 return authenticationFailureUrl;
443 }
444
445 public void setAuthenticationFailureUrl(String authenticationFailureUrl) {
446 this.authenticationFailureUrl = authenticationFailureUrl;
447 }
448
449 protected AuthenticationManager getAuthenticationManager() {
450 return authenticationManager;
451 }
452
453 public void setAuthenticationManager(AuthenticationManager authenticationManager) {
454 this.authenticationManager = authenticationManager;
455 }
456
457
458
459
460
461
462
463 public abstract String getDefaultFilterProcessesUrl();
464
465
466
467
468
469
470
471
472
473
474 public String getDefaultTargetUrl() {
475 return defaultTargetUrl;
476 }
477
478 public void setDefaultTargetUrl(String defaultTargetUrl) {
479 Assert.isTrue(defaultTargetUrl.startsWith("/") | defaultTargetUrl.startsWith("http"),
480 "defaultTarget must start with '/' or with 'http(s)'");
481 this.defaultTargetUrl = defaultTargetUrl;
482 }
483
484 Properties getExceptionMappings() {
485 return new Properties(exceptionMappings);
486 }
487
488 public void setExceptionMappings(Properties exceptionMappings) {
489 this.exceptionMappings = exceptionMappings;
490 }
491
492 public String getFilterProcessesUrl() {
493 return filterProcessesUrl;
494 }
495
496 public void setFilterProcessesUrl(String filterProcessesUrl) {
497 this.filterProcessesUrl = filterProcessesUrl;
498 }
499
500 public RememberMeServices getRememberMeServices() {
501 return rememberMeServices;
502 }
503
504 public void setRememberMeServices(RememberMeServices rememberMeServices) {
505 this.rememberMeServices = rememberMeServices;
506 }
507
508 boolean isAlwaysUseDefaultTargetUrl() {
509 return alwaysUseDefaultTargetUrl;
510 }
511
512 public void setAlwaysUseDefaultTargetUrl(boolean alwaysUseDefaultTargetUrl) {
513 this.alwaysUseDefaultTargetUrl = alwaysUseDefaultTargetUrl;
514 }
515
516 public void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {
517 this.continueChainBeforeSuccessfulAuthentication = continueChainBeforeSuccessfulAuthentication;
518 }
519
520 public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
521 this.eventPublisher = eventPublisher;
522 }
523
524 public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
525 Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
526 this.authenticationDetailsSource = authenticationDetailsSource;
527 }
528
529 public void setMessageSource(MessageSource messageSource) {
530 this.messages = new MessageSourceAccessor(messageSource);
531 }
532
533 public void setInvalidateSessionOnSuccessfulAuthentication(boolean invalidateSessionOnSuccessfulAuthentication) {
534 this.invalidateSessionOnSuccessfulAuthentication = invalidateSessionOnSuccessfulAuthentication;
535 }
536
537 public void setMigrateInvalidatedSessionAttributes(boolean migrateInvalidatedSessionAttributes) {
538 this.migrateInvalidatedSessionAttributes = migrateInvalidatedSessionAttributes;
539 }
540
541 public AuthenticationDetailsSource getAuthenticationDetailsSource() {
542
543 return authenticationDetailsSource;
544 }
545
546 public void setUseRelativeContext(boolean useRelativeContext) {
547 this.useRelativeContext = useRelativeContext;
548 }
549
550 protected boolean getAllowSessionCreation() {
551 return allowSessionCreation;
552 }
553
554 public void setAllowSessionCreation(boolean allowSessionCreation) {
555 this.allowSessionCreation = allowSessionCreation;
556 }
557
558
559
560
561 protected TargetUrlResolver getTargetUrlResolver() {
562 return targetUrlResolver;
563 }
564
565
566
567
568 public void setTargetUrlResolver(TargetUrlResolver targetUrlResolver) {
569 this.targetUrlResolver = targetUrlResolver;
570 }
571
572
573
574
575
576
577 public void setServerSideRedirect(boolean serverSideRedirect) {
578 this.serverSideRedirect = serverSideRedirect;
579 }
580
581
582
583
584
585 public void setSessionRegistry(SessionRegistry sessionRegistry) {
586 this.sessionRegistry = sessionRegistry;
587 }
588 }