浏览代码

SEC-2549: Remove LazyBean marker interface

Rob Winch 11 年之前
父节点
当前提交
37bb350883

+ 0 - 3
config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java

@@ -115,7 +115,6 @@ public class AuthenticationConfiguration {
         ProxyFactoryBean proxyFactory = new ProxyFactoryBean();
         ProxyFactoryBean proxyFactory = new ProxyFactoryBean();
         proxyFactory = objectPostProcessor.postProcess(proxyFactory);
         proxyFactory = objectPostProcessor.postProcess(proxyFactory);
         proxyFactory.setTargetSource(lazyTargetSource);
         proxyFactory.setTargetSource(lazyTargetSource);
-        proxyFactory.setInterfaces(new Class[] { interfaceName, LazyBean.class });
         return (T) proxyFactory.getObject();
         return (T) proxyFactory.getObject();
     }
     }
 
 
@@ -123,8 +122,6 @@ public class AuthenticationConfiguration {
         return lazyBean(AuthenticationManager.class);
         return lazyBean(AuthenticationManager.class);
     }
     }
 
 
-    private interface LazyBean {}
-
     private static class EnableGlobalAuthenticationAutowiredConfigurer extends GlobalAuthenticationConfigurerAdapter {
     private static class EnableGlobalAuthenticationAutowiredConfigurer extends GlobalAuthenticationConfigurerAdapter {
         private final ApplicationContext context;
         private final ApplicationContext context;
         private static final Log logger = LogFactory.getLog(EnableGlobalAuthenticationAutowiredConfigurer.class);
         private static final Log logger = LogFactory.getLog(EnableGlobalAuthenticationAutowiredConfigurer.class);

+ 28 - 11
config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.java

@@ -18,11 +18,18 @@ package org.springframework.security.config.annotation.web.configuration;
 
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Field;
 import java.util.Arrays;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.List;
+import java.util.Set;
 
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.commons.logging.LogFactory;
+import org.springframework.aop.TargetSource;
+import org.springframework.aop.framework.Advised;
+import org.springframework.aop.target.LazyInitTargetSource;
 import org.springframework.beans.FatalBeanException;
 import org.springframework.beans.FatalBeanException;
+import org.springframework.beans.factory.BeanFactoryUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContext;
 import org.springframework.core.annotation.Order;
 import org.springframework.core.annotation.Order;
@@ -210,7 +217,7 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
      * @throws Exception
      * @throws Exception
      */
      */
     public AuthenticationManager authenticationManagerBean() throws Exception {
     public AuthenticationManager authenticationManagerBean() throws Exception {
-        return new AuthenticationManagerDelegator(authenticationBuilder);
+        return new AuthenticationManagerDelegator(authenticationBuilder, context);
     }
     }
 
 
     /**
     /**
@@ -413,12 +420,14 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
         private AuthenticationManagerBuilder delegateBuilder;
         private AuthenticationManagerBuilder delegateBuilder;
         private AuthenticationManager delegate;
         private AuthenticationManager delegate;
         private final Object delegateMonitor = new Object();
         private final Object delegateMonitor = new Object();
+        private Set<String> beanNames;
 
 
-        AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder) {
+        AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder, ApplicationContext context) {
             Assert.notNull(delegateBuilder,"delegateBuilder cannot be null");
             Assert.notNull(delegateBuilder,"delegateBuilder cannot be null");
             Field parentAuthMgrField = ReflectionUtils.findField(AuthenticationManagerBuilder.class, "parentAuthenticationManager");
             Field parentAuthMgrField = ReflectionUtils.findField(AuthenticationManagerBuilder.class, "parentAuthenticationManager");
             ReflectionUtils.makeAccessible(parentAuthMgrField);
             ReflectionUtils.makeAccessible(parentAuthMgrField);
-            validateBeanCycle(ReflectionUtils.getField(parentAuthMgrField, delegateBuilder));
+            beanNames = getAuthenticationManagerBeanNames(context);
+            validateBeanCycle(ReflectionUtils.getField(parentAuthMgrField, delegateBuilder), beanNames);
             this.delegateBuilder = delegateBuilder;
             this.delegateBuilder = delegateBuilder;
         }
         }
 
 
@@ -437,16 +446,24 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
             return delegate.authenticate(authentication);
             return delegate.authenticate(authentication);
         }
         }
 
 
-        private static void validateBeanCycle(Object auth) {
-            if(auth != null) {
-                String lazyBeanClassName = AuthenticationConfiguration.class.getName() + "$LazyBean";
-                Class<?>[] interfaces = auth.getClass().getInterfaces();
-                for(Class<?> i : interfaces) {
-                    String className = i.getName();
-                    if(className.equals(lazyBeanClassName)) {
-                        throw new FatalBeanException("A dependency cycle was detected when trying to resolve the AuthenticationManager. Please ensure you have configured authentication.");
+        private static Set<String> getAuthenticationManagerBeanNames(ApplicationContext applicationContext) {
+             String[] beanNamesForType = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, AuthenticationManager.class);
+             return new HashSet<String>(Arrays.asList(beanNamesForType));
+        }
+
+        private static void validateBeanCycle(Object auth, Set<String> beanNames) {
+            if(auth != null && !beanNames.isEmpty()) {
+                if(auth instanceof Advised){
+                    Advised advised = (Advised) auth;
+                    TargetSource targetSource = advised.getTargetSource();
+                    if(targetSource instanceof LazyInitTargetSource) {
+                        LazyInitTargetSource lits = (LazyInitTargetSource) targetSource;
+                        if(beanNames.contains(lits.getTargetBeanName())) {
+                            throw new FatalBeanException("A dependency cycle was detected when trying to resolve the AuthenticationManager. Please ensure you have configured authentication.");
+                        }
                     }
                     }
                 }
                 }
+                beanNames = Collections.emptySet();
             }
             }
         }
         }
     }
     }

+ 2 - 1
config/src/test/groovy/org/springframework/security/config/annotation/BaseSpringSpec.groovy

@@ -123,7 +123,8 @@ abstract class BaseSpringSpec extends Specification {
     AuthenticationManager getAuthenticationManager() {
     AuthenticationManager getAuthenticationManager() {
         try {
         try {
             authenticationManager().delegateBuilder.getObject()
             authenticationManager().delegateBuilder.getObject()
-        } catch(NoSuchBeanDefinitionException e) {}
+        } catch(NoSuchBeanDefinitionException e) {
+        } catch(MissingPropertyException e) {}
         findFilter(FilterSecurityInterceptor).authenticationManager
         findFilter(FilterSecurityInterceptor).authenticationManager
     }
     }
 
 

+ 45 - 0
config/src/test/groovy/org/springframework/security/config/annotation/web/configuration/Sec2515Tests.groovy

@@ -15,10 +15,13 @@
  */
  */
 package org.springframework.security.config.annotation.web.configuration;
 package org.springframework.security.config.annotation.web.configuration;
 
 
+import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.beans.FatalBeanException;
 import org.springframework.beans.FatalBeanException;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext
 import org.springframework.context.annotation.Bean
 import org.springframework.context.annotation.Bean
 import org.springframework.context.annotation.Configuration
 import org.springframework.context.annotation.Configuration
 import org.springframework.security.authentication.AuthenticationManager
 import org.springframework.security.authentication.AuthenticationManager
+import org.springframework.security.authentication.TestingAuthenticationToken
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
 import org.springframework.security.config.annotation.BaseSpringSpec
 import org.springframework.security.config.annotation.BaseSpringSpec
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
@@ -44,6 +47,48 @@ public class Sec2515Tests extends BaseSpringSpec {
         }
         }
     }
     }
 
 
+    def "Custom Name Prevent StackOverflow with bean graph cycle"() {
+        when:
+           loadConfig(StackOverflowSecurityConfig)
+        then:
+            thrown(FatalBeanException)
+    }
+
+    @EnableWebSecurity
+    @Configuration
+    static class CustomBeanNameStackOverflowSecurityConfig extends WebSecurityConfigurerAdapter {
+
+        @Override
+        @Bean(name="custom")
+        public AuthenticationManager authenticationManagerBean()
+                throws Exception {
+            return super.authenticationManagerBean();
+        }
+    }
+
+    def "SEC-2549: Can load with child classloader"() {
+        setup:
+            CanLoadWithChildConfig.AM = Mock(AuthenticationManager)
+            context = new AnnotationConfigApplicationContext()
+            context.classLoader = new URLClassLoader(new URL[0], context.classLoader)
+            context.register(CanLoadWithChildConfig)
+            context.refresh()
+        when:
+            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("user", "password"))
+        then:
+            noExceptionThrown()
+            1 * CanLoadWithChildConfig.AM.authenticate(_) >> new TestingAuthenticationToken("user","password","ROLE_USER")
+    }
+
+    @EnableWebSecurity
+    @Configuration
+    static class CanLoadWithChildConfig extends WebSecurityConfigurerAdapter {
+        static AuthenticationManager AM
+        @Bean
+        public AuthenticationManager am() {
+            AM
+        }
+    }
 
 
     def "SEC-2515: @Bean still works when configure(AuthenticationManagerBuilder) used"() {
     def "SEC-2515: @Bean still works when configure(AuthenticationManagerBuilder) used"() {
         when:
         when: