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