Selaa lähdekoodia

SEC-120: Remember-me to delegate to AuthenticationManager so authentication-specific behaviour (such as concurrent user management) can be applied.

Ben Alex 19 vuotta sitten
vanhempi
commit
c8c7c24822

+ 67 - 27
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.Authentication;
+import org.acegisecurity.AuthenticationException;
+import org.acegisecurity.AuthenticationManager;
+
 import org.acegisecurity.context.SecurityContextHolder;
+
 import org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent;
-import org.acegisecurity.Authentication;
 
 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
  * <code>Authentication</code> or <code>null</code> returned by that method
- * will be placed into the <code>SecurityContext</code>.
+ * will be placed into the <code>SecurityContext</code>. The
+ * <code>AuthenticationManager</code> 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 <code>AuthenticationManager</code> as part of their
+ * contract.
  * </p>
  * 
  * <p>
  * 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 <code>AuthenticationManager</code>-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 <code>AuthenticationManager</code>-specific application
+ * event.
  * </p>
  * 
  * <p>
@@ -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) {
+                // 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);
+                }
 
-            if(rememberMeAuth != null) {
-                SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);
+                // 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;
+    }
 }

+ 45 - 19
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 {

+ 1 - 0
samples/contacts/src/main/webapp/filter/WEB-INF/applicationContext-acegi-security.xml

@@ -90,6 +90,7 @@
    </bean>
 
    <bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
+      <property name="authenticationManager"><ref local="authenticationManager"/></property>
       <property name="rememberMeServices"><ref local="rememberMeServices"/></property>
    </bean>