diff --git a/core/src/main/java/org/acegisecurity/ui/rememberme/RememberMeProcessingFilter.java b/core/src/main/java/org/acegisecurity/ui/rememberme/RememberMeProcessingFilter.java index 0bb9a8ec70..4169620a14 100644 --- a/core/src/main/java/org/acegisecurity/ui/rememberme/RememberMeProcessingFilter.java +++ b/core/src/main/java/org/acegisecurity/ui/rememberme/RememberMeProcessingFilter.java @@ -1,4 +1,4 @@ -/* Copyright 2004, 2005 Acegi Technology Pty Limited +/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +15,13 @@ package org.acegisecurity.ui.rememberme; -import org.acegisecurity.context.SecurityContextHolder; -import org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent; import org.acegisecurity.Authentication; +import org.acegisecurity.AuthenticationException; +import org.acegisecurity.AuthenticationManager; + +import org.acegisecurity.context.SecurityContextHolder; + +import org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -54,15 +58,21 @@ import javax.servlet.http.HttpServletResponse; * org.acegisecurity.ui.rememberme.RememberMeServices#autoLogin(HttpServletRequest, * HttpServletResponse)} method called by this filter. The * Authentication or null returned by that method - * will be placed into the SecurityContext. + * will be placed into the SecurityContext. The + * AuthenticationManager will be used, so that any concurrent + * session management or other authentication-specific behaviour can be + * achieved. This is the same pattern as with other authentication mechanisms, + * which call the AuthenticationManager as part of their + * contract. *

* *

* If authentication is successful, an {@link - * org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent} will be - * published to the application context. No events will be published if - * authentication was unsuccessful, because this would generally be recorded - * via an AuthenticationManager-specific application event. + * org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent} + * will be published to the application context. No events will be published + * if authentication was unsuccessful, because this would generally be + * recorded via an AuthenticationManager-specific application + * event. *

* *

@@ -75,7 +85,7 @@ import javax.servlet.http.HttpServletResponse; * @version $Id$ */ public class RememberMeProcessingFilter implements Filter, InitializingBean, - ApplicationEventPublisherAware { + ApplicationEventPublisherAware { //~ Static fields/initializers ============================================= private static final Log logger = LogFactory.getLog(RememberMeProcessingFilter.class); @@ -83,24 +93,14 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean, //~ Instance fields ======================================================== private ApplicationEventPublisher eventPublisher; + private AuthenticationManager authenticationManager; private RememberMeServices rememberMeServices = new NullRememberMeServices(); //~ Methods ================================================================ - public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) { - this.eventPublisher = eventPublisher; - } - - public void setRememberMeServices(RememberMeServices rememberMeServices) { - this.rememberMeServices = rememberMeServices; - } - - public RememberMeServices getRememberMeServices() { - return rememberMeServices; - } - public void afterPropertiesSet() throws Exception { - Assert.notNull(rememberMeServices); + Assert.notNull(rememberMeServices, "RememberMeServices required"); + Assert.notNull(authenticationManager, "AuthenticationManager required"); } /** @@ -122,11 +122,29 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean, HttpServletResponse httpResponse = (HttpServletResponse) response; if (SecurityContextHolder.getContext().getAuthentication() == null) { - Authentication rememberMeAuth = - rememberMeServices.autoLogin(httpRequest, httpResponse); + Authentication rememberMeAuth = rememberMeServices.autoLogin(httpRequest, + httpResponse); - if(rememberMeAuth != null) { - SecurityContextHolder.getContext().setAuthentication(rememberMeAuth); + if (rememberMeAuth != null) { + // Attempt authenticaton via AuthenticationManager + try { + authenticationManager.authenticate(rememberMeAuth); + } catch (AuthenticationException authenticationException) { + if (logger.isDebugEnabled()) { + logger.debug( + "SecurityContextHolder not populated with remember-me token, as AuthenticationManager rejected Authentication returned by RememberMeServices: '" + + rememberMeAuth + + "'; invalidating remember-me token", + authenticationException); + } + + rememberMeServices.loginFail(httpRequest, httpResponse); + chain.doFilter(request, response); + } + + // Store to SecurityContextHolder + SecurityContextHolder.getContext() + .setAuthentication(rememberMeAuth); if (logger.isDebugEnabled()) { logger.debug( @@ -138,9 +156,12 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean, // Fire event if (this.eventPublisher != null) { eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent( - SecurityContextHolder.getContext().getAuthentication(), + SecurityContextHolder.getContext() + .getAuthentication(), this.getClass())); } + + chain.doFilter(request, response); } } else { if (logger.isDebugEnabled()) { @@ -149,9 +170,13 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean, + SecurityContextHolder.getContext().getAuthentication() + "'"); } - } - chain.doFilter(request, response); + chain.doFilter(request, response); + } + } + + public RememberMeServices getRememberMeServices() { + return rememberMeServices; } /** @@ -159,6 +184,21 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean, * * @param ignored not used * + * @throws ServletException DOCUMENT ME! */ public void init(FilterConfig ignored) throws ServletException {} + + public void setApplicationEventPublisher( + ApplicationEventPublisher eventPublisher) { + this.eventPublisher = eventPublisher; + } + + public void setAuthenticationManager( + AuthenticationManager authenticationManager) { + this.authenticationManager = authenticationManager; + } + + public void setRememberMeServices(RememberMeServices rememberMeServices) { + this.rememberMeServices = rememberMeServices; + } } diff --git a/core/src/test/java/org/acegisecurity/ui/rememberme/RememberMeProcessingFilterTests.java b/core/src/test/java/org/acegisecurity/ui/rememberme/RememberMeProcessingFilterTests.java index ff8a6bf14d..d37d16d2ab 100644 --- a/core/src/test/java/org/acegisecurity/ui/rememberme/RememberMeProcessingFilterTests.java +++ b/core/src/test/java/org/acegisecurity/ui/rememberme/RememberMeProcessingFilterTests.java @@ -1,4 +1,4 @@ -/* Copyright 2004, 2005 Acegi Technology Pty Limited +/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,9 +20,12 @@ import junit.framework.TestCase; import org.acegisecurity.Authentication; import org.acegisecurity.GrantedAuthority; import org.acegisecurity.GrantedAuthorityImpl; +import org.acegisecurity.MockAuthenticationManager; import org.acegisecurity.MockFilterConfig; + import org.acegisecurity.context.SecurityContextHolder; import org.acegisecurity.context.SecurityContextImpl; + import org.acegisecurity.providers.TestingAuthenticationToken; import org.springframework.mock.web.MockHttpServletRequest; @@ -59,13 +62,50 @@ public class RememberMeProcessingFilterTests extends TestCase { //~ Methods ================================================================ + private void executeFilterInContainerSimulator(FilterConfig filterConfig, + Filter filter, ServletRequest request, ServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + filter.init(filterConfig); + filter.doFilter(request, response, filterChain); + filter.destroy(); + } + public static void main(String[] args) { junit.textui.TestRunner.run(RememberMeProcessingFilterTests.class); } + protected void setUp() throws Exception { + super.setUp(); + SecurityContextHolder.setContext(new SecurityContextImpl()); + } + + protected void tearDown() throws Exception { + super.tearDown(); + SecurityContextHolder.setContext(new SecurityContextImpl()); + } + + public void testDetectsAuthenticationManagerProperty() + throws Exception { + RememberMeProcessingFilter filter = new RememberMeProcessingFilter(); + filter.setAuthenticationManager(new MockAuthenticationManager()); + + filter.afterPropertiesSet(); + assertTrue(true); + + filter.setAuthenticationManager(null); + + try { + filter.afterPropertiesSet(); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + assertTrue(true); + } + } + public void testDetectsRememberMeServicesProperty() throws Exception { RememberMeProcessingFilter filter = new RememberMeProcessingFilter(); + filter.setAuthenticationManager(new MockAuthenticationManager()); // check default is NullRememberMeServices assertEquals(NullRememberMeServices.class, @@ -90,6 +130,7 @@ public class RememberMeProcessingFilterTests extends TestCase { public void testDoFilterWithNonHttpServletRequestDetected() throws Exception { RememberMeProcessingFilter filter = new RememberMeProcessingFilter(); + filter.setAuthenticationManager(new MockAuthenticationManager()); try { filter.doFilter(null, new MockHttpServletResponse(), @@ -104,6 +145,7 @@ public class RememberMeProcessingFilterTests extends TestCase { public void testDoFilterWithNonHttpServletResponseDetected() throws Exception { RememberMeProcessingFilter filter = new RememberMeProcessingFilter(); + filter.setAuthenticationManager(new MockAuthenticationManager()); try { MockHttpServletRequest request = new MockHttpServletRequest(); @@ -129,6 +171,7 @@ public class RememberMeProcessingFilterTests extends TestCase { "password", new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_REMEMBERED")}); RememberMeProcessingFilter filter = new RememberMeProcessingFilter(); + filter.setAuthenticationManager(new MockAuthenticationManager()); filter.setRememberMeServices(new MockRememberMeServices(remembered)); filter.afterPropertiesSet(); @@ -149,6 +192,7 @@ public class RememberMeProcessingFilterTests extends TestCase { "password", new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_REMEMBERED")}); RememberMeProcessingFilter filter = new RememberMeProcessingFilter(); + filter.setAuthenticationManager(new MockAuthenticationManager()); filter.setRememberMeServices(new MockRememberMeServices(remembered)); filter.afterPropertiesSet(); @@ -162,24 +206,6 @@ public class RememberMeProcessingFilterTests extends TestCase { SecurityContextHolder.getContext().getAuthentication()); } - protected void setUp() throws Exception { - super.setUp(); - SecurityContextHolder.setContext(new SecurityContextImpl()); - } - - protected void tearDown() throws Exception { - super.tearDown(); - SecurityContextHolder.setContext(new SecurityContextImpl()); - } - - private void executeFilterInContainerSimulator(FilterConfig filterConfig, - Filter filter, ServletRequest request, ServletResponse response, - FilterChain filterChain) throws ServletException, IOException { - filter.init(filterConfig); - filter.doFilter(request, response, filterChain); - filter.destroy(); - } - //~ Inner Classes ========================================================== private class MockFilterChain implements FilterChain { diff --git a/samples/contacts/src/main/webapp/filter/WEB-INF/applicationContext-acegi-security.xml b/samples/contacts/src/main/webapp/filter/WEB-INF/applicationContext-acegi-security.xml index d5883c0784..765ebb0987 100644 --- a/samples/contacts/src/main/webapp/filter/WEB-INF/applicationContext-acegi-security.xml +++ b/samples/contacts/src/main/webapp/filter/WEB-INF/applicationContext-acegi-security.xml @@ -90,6 +90,7 @@ +