SEC-2515: Detect object cycle for AuthenticationManager configuration
This commit is contained in:
parent
32d3e29c65
commit
e4a58375cc
|
@ -114,7 +114,7 @@ public class AuthenticationConfiguration {
|
||||||
lazyTargetSource.setBeanFactory(applicationContext);
|
lazyTargetSource.setBeanFactory(applicationContext);
|
||||||
ProxyFactoryBean proxyFactory = new ProxyFactoryBean();
|
ProxyFactoryBean proxyFactory = new ProxyFactoryBean();
|
||||||
proxyFactory.setTargetSource(lazyTargetSource);
|
proxyFactory.setTargetSource(lazyTargetSource);
|
||||||
proxyFactory.setInterfaces(new Class[] { interfaceName });
|
proxyFactory.setInterfaces(new Class[] { interfaceName, LazyBean.class });
|
||||||
return (T) proxyFactory.getObject();
|
return (T) proxyFactory.getObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +122,7 @@ 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;
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
package org.springframework.security.config.annotation.web.configuration;
|
package org.springframework.security.config.annotation.web.configuration;
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
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.beans.FatalBeanException;
|
||||||
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;
|
||||||
|
@ -44,6 +46,7 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||||
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.web.accept.ContentNegotiationStrategy;
|
import org.springframework.web.accept.ContentNegotiationStrategy;
|
||||||
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||||
|
|
||||||
|
@ -71,8 +74,8 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
|
||||||
|
|
||||||
private AuthenticationConfiguration authenticationConfiguration;
|
private AuthenticationConfiguration authenticationConfiguration;
|
||||||
private AuthenticationManagerBuilder authenticationBuilder;
|
private AuthenticationManagerBuilder authenticationBuilder;
|
||||||
private AuthenticationManagerBuilder parentAuthenticationBuilder;
|
private AuthenticationManagerBuilder localConfigureAuthenticationBldr;
|
||||||
private boolean disableAuthenticationRegistration;
|
private boolean disableLocalConfigureAuthenticationBldr;
|
||||||
private boolean authenticationManagerInitialized;
|
private boolean authenticationManagerInitialized;
|
||||||
private AuthenticationManager authenticationManager;
|
private AuthenticationManager authenticationManager;
|
||||||
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
||||||
|
@ -148,7 +151,7 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
this.disableAuthenticationRegistration = true;
|
this.disableLocalConfigureAuthenticationBldr = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -163,11 +166,11 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
|
||||||
}
|
}
|
||||||
|
|
||||||
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
|
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
|
||||||
parentAuthenticationBuilder.authenticationEventPublisher(eventPublisher);
|
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
|
||||||
|
|
||||||
AuthenticationManager authenticationManager = authenticationManager();
|
AuthenticationManager authenticationManager = authenticationManager();
|
||||||
authenticationBuilder.parentAuthenticationManager(authenticationManager);
|
authenticationBuilder.parentAuthenticationManager(authenticationManager);
|
||||||
http = new HttpSecurity(objectPostProcessor,authenticationBuilder, parentAuthenticationBuilder.getSharedObjects());
|
http = new HttpSecurity(objectPostProcessor,authenticationBuilder, localConfigureAuthenticationBldr.getSharedObjects());
|
||||||
http.setSharedObject(UserDetailsService.class, userDetailsService());
|
http.setSharedObject(UserDetailsService.class, userDetailsService());
|
||||||
http.setSharedObject(ApplicationContext.class, context);
|
http.setSharedObject(ApplicationContext.class, context);
|
||||||
http.setSharedObject(ContentNegotiationStrategy.class, contentNegotiationStrategy);
|
http.setSharedObject(ContentNegotiationStrategy.class, contentNegotiationStrategy);
|
||||||
|
@ -221,11 +224,11 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
|
||||||
*/
|
*/
|
||||||
protected AuthenticationManager authenticationManager() throws Exception {
|
protected AuthenticationManager authenticationManager() throws Exception {
|
||||||
if(!authenticationManagerInitialized) {
|
if(!authenticationManagerInitialized) {
|
||||||
configure(parentAuthenticationBuilder);
|
configure(localConfigureAuthenticationBldr);
|
||||||
if(disableAuthenticationRegistration) {
|
if(disableLocalConfigureAuthenticationBldr) {
|
||||||
authenticationManager = authenticationConfiguration.getAuthenticationManager();
|
authenticationManager = authenticationConfiguration.getAuthenticationManager();
|
||||||
} else {
|
} else {
|
||||||
authenticationManager = parentAuthenticationBuilder.build();
|
authenticationManager = localConfigureAuthenticationBldr.build();
|
||||||
}
|
}
|
||||||
authenticationManagerInitialized = true;
|
authenticationManagerInitialized = true;
|
||||||
}
|
}
|
||||||
|
@ -253,7 +256,7 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
|
||||||
*/
|
*/
|
||||||
public UserDetailsService userDetailsServiceBean() throws Exception {
|
public UserDetailsService userDetailsServiceBean() throws Exception {
|
||||||
AuthenticationManagerBuilder globalAuthBuilder = context.getBean(AuthenticationManagerBuilder.class);
|
AuthenticationManagerBuilder globalAuthBuilder = context.getBean(AuthenticationManagerBuilder.class);
|
||||||
return new UserDetailsServiceDelegator(Arrays.asList(parentAuthenticationBuilder, globalAuthBuilder));
|
return new UserDetailsServiceDelegator(Arrays.asList(localConfigureAuthenticationBldr, globalAuthBuilder));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -266,7 +269,7 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
|
||||||
*/
|
*/
|
||||||
protected UserDetailsService userDetailsService() {
|
protected UserDetailsService userDetailsService() {
|
||||||
AuthenticationManagerBuilder globalAuthBuilder = context.getBean(AuthenticationManagerBuilder.class);
|
AuthenticationManagerBuilder globalAuthBuilder = context.getBean(AuthenticationManagerBuilder.class);
|
||||||
return new UserDetailsServiceDelegator(Arrays.asList(parentAuthenticationBuilder, globalAuthBuilder));
|
return new UserDetailsServiceDelegator(Arrays.asList(localConfigureAuthenticationBldr, globalAuthBuilder));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(final WebSecurity web) throws Exception {
|
public void init(final WebSecurity web) throws Exception {
|
||||||
|
@ -337,7 +340,7 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
|
||||||
this.objectPostProcessor = objectPostProcessor;
|
this.objectPostProcessor = objectPostProcessor;
|
||||||
|
|
||||||
authenticationBuilder = new AuthenticationManagerBuilder(objectPostProcessor);
|
authenticationBuilder = new AuthenticationManagerBuilder(objectPostProcessor);
|
||||||
parentAuthenticationBuilder = new AuthenticationManagerBuilder(objectPostProcessor) {
|
localConfigureAuthenticationBldr = new AuthenticationManagerBuilder(objectPostProcessor) {
|
||||||
@Override
|
@Override
|
||||||
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
|
public AuthenticationManagerBuilder eraseCredentials(boolean eraseCredentials) {
|
||||||
authenticationBuilder.eraseCredentials(eraseCredentials);
|
authenticationBuilder.eraseCredentials(eraseCredentials);
|
||||||
|
@ -413,6 +416,9 @@ public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigu
|
||||||
|
|
||||||
AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder) {
|
AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder) {
|
||||||
Assert.notNull(delegateBuilder,"delegateBuilder cannot be null");
|
Assert.notNull(delegateBuilder,"delegateBuilder cannot be null");
|
||||||
|
Field parentAuthMgrField = ReflectionUtils.findField(AuthenticationManagerBuilder.class, "parentAuthenticationManager");
|
||||||
|
ReflectionUtils.makeAccessible(parentAuthMgrField);
|
||||||
|
validateBeanCycle(ReflectionUtils.getField(parentAuthMgrField, delegateBuilder));
|
||||||
this.delegateBuilder = delegateBuilder;
|
this.delegateBuilder = delegateBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,5 +436,18 @@ 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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2013 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.springframework.security.config.annotation.web.configuration;
|
||||||
|
|
||||||
|
import org.springframework.beans.FatalBeanException;
|
||||||
|
import org.springframework.context.annotation.Bean
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||||
|
import org.springframework.security.config.annotation.BaseSpringSpec
|
||||||
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||||
|
|
||||||
|
public class Sec2515Tests extends BaseSpringSpec {
|
||||||
|
|
||||||
|
def "SEC-2515: Prevent StackOverflow with bean graph cycle"() {
|
||||||
|
when:
|
||||||
|
loadConfig(StackOverflowSecurityConfig)
|
||||||
|
then:
|
||||||
|
thrown(FatalBeanException)
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
static class StackOverflowSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Bean
|
||||||
|
public AuthenticationManager authenticationManagerBean()
|
||||||
|
throws Exception {
|
||||||
|
return super.authenticationManagerBean();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def "SEC-2515: @Bean still works when configure(AuthenticationManagerBuilder) used"() {
|
||||||
|
when:
|
||||||
|
loadConfig(SecurityConfig)
|
||||||
|
then:
|
||||||
|
noExceptionThrown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Configuration
|
||||||
|
static class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Bean
|
||||||
|
public AuthenticationManager authenticationManagerBean()
|
||||||
|
throws Exception {
|
||||||
|
return super.authenticationManagerBean();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(AuthenticationManagerBuilder auth)
|
||||||
|
throws Exception {
|
||||||
|
auth.inMemoryAuthentication()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue