瀏覽代碼

Modify contract of AuthenticationProvider to allow AuthenticationProvider implementations to return null if they do not wish to process a given Authentication request, despite asserting they support it.

Ben Alex 21 年之前
父節點
當前提交
872ace9164

+ 20 - 4
core/src/main/java/org/acegisecurity/providers/AuthenticationProvider.java

@@ -35,7 +35,12 @@ public interface AuthenticationProvider {
      *
      * @param authentication the authentication request object.
      *
-     * @return a fully authenticated object including credentials.
+     * @return a fully authenticated object including credentials. May return
+     *         <code>null</code> if the <code>AuthenticationProvider</code> is
+     *         unable to support authentication of the passed
+     *         <code>Authentication</code> object. In such a case, the next
+     *         <code>AuthenticationProvider</code> that supports the presented
+     *         <code>Authentication</code> class will be tried.
      *
      * @throws AuthenticationException if authentication fails.
      */
@@ -43,8 +48,18 @@ public interface AuthenticationProvider {
         throws AuthenticationException;
 
     /**
-     * Returns true if this <Code>AuthenticationProvider</code> supports the
-     * indicated <Code>Authentication</code> object.
+     * Returns <code>true</code> if this <Code>AuthenticationProvider</code>
+     * supports the indicated <Code>Authentication</code> object.
+     * 
+     * <p>
+     * Returning <code>true</code> does not guarantee an
+     * <code>AuthenticationProvider</code> will be able to authenticate the
+     * presented instance of the <code>Authentication</code> class. It simply
+     * indicates it can support closer evaluation of it. An
+     * <code>AuthenticationProvider</code> can still return <code>null</code>
+     * from the {@link #authenticate(Authentication)} method to indicate
+     * another <code>AuthenticationProvider</code> should be tried.
+     * </p>
      * 
      * <P>
      * Selection of an <code>AuthenticationProvider</code> capable of
@@ -52,7 +67,8 @@ public interface AuthenticationProvider {
      * <code>ProviderManager</code>.
      * </p>
      *
-     * @return DOCUMENT ME!
+     * @return <code>true</code> if the implementation can more closely
+     *         evaluate the <code>Authentication</code> class presented
      */
     public boolean supports(Class authentication);
 }

+ 5 - 1
core/src/main/java/org/acegisecurity/providers/ProviderManager.java

@@ -123,7 +123,11 @@ public class ProviderManager implements InitializingBean, AuthenticationManager
                 logger.debug("Authentication attempt using "
                     + provider.getClass().getName());
 
-                return provider.authenticate(authentication);
+                Authentication result = provider.authenticate(authentication);
+
+                if (result != null) {
+                    return result;
+                }
             }
         }
 

+ 54 - 0
core/src/test/java/org/acegisecurity/providers/ProviderManagerTests.java

@@ -90,6 +90,26 @@ public class ProviderManagerTests extends TestCase {
         assertEquals("ROLE_TWO", castResult.getAuthorities()[1].getAuthority());
     }
 
+    public void testAuthenticationSuccessWhenFirstProviderReturnsNullButSecondAuthenticates() {
+        TestingAuthenticationToken token = new TestingAuthenticationToken("Test",
+                "Password",
+                new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ONE"), new GrantedAuthorityImpl(
+                        "ROLE_TWO")});
+
+        ProviderManager mgr = makeProviderManagerWithMockProviderWhichReturnsNullInList();
+        Authentication result = mgr.authenticate(token);
+
+        if (!(result instanceof TestingAuthenticationToken)) {
+            fail("Should have returned instance of TestingAuthenticationToken");
+        }
+
+        TestingAuthenticationToken castResult = (TestingAuthenticationToken) result;
+        assertEquals("Test", castResult.getPrincipal());
+        assertEquals("Password", castResult.getCredentials());
+        assertEquals("ROLE_ONE", castResult.getAuthorities()[0].getAuthority());
+        assertEquals("ROLE_TWO", castResult.getAuthorities()[1].getAuthority());
+    }
+
     public void testStartupFailsIfProviderListDoesNotContainingProviders()
         throws Exception {
         List providers = new Vector();
@@ -146,6 +166,19 @@ public class ProviderManagerTests extends TestCase {
         return mgr;
     }
 
+    private ProviderManager makeProviderManagerWithMockProviderWhichReturnsNullInList() {
+        MockProviderWhichReturnsNull provider1 = new MockProviderWhichReturnsNull();
+        MockProvider provider2 = new MockProvider();
+        List providers = new Vector();
+        providers.add(provider1);
+        providers.add(provider2);
+
+        ProviderManager mgr = new ProviderManager();
+        mgr.setProviders(providers);
+
+        return mgr;
+    }
+
     //~ Inner Classes ==========================================================
 
     private class MockProvider implements AuthenticationProvider {
@@ -168,4 +201,25 @@ public class ProviderManagerTests extends TestCase {
             }
         }
     }
+
+    private class MockProviderWhichReturnsNull implements AuthenticationProvider {
+        public Authentication authenticate(Authentication authentication)
+            throws AuthenticationException {
+            if (supports(authentication.getClass())) {
+                return null;
+            } else {
+                throw new AuthenticationServiceException(
+                    "Don't support this class");
+            }
+        }
+
+        public boolean supports(Class authentication) {
+            if (TestingAuthenticationToken.class.isAssignableFrom(
+                    authentication)) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
 }