Skip to content

Commit

Permalink
Trap infinite loop in lazy proxies
Browse files Browse the repository at this point in the history
  • Loading branch information
Dave Syer committed Apr 15, 2014
1 parent 092dabb commit d58d503
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@

import javax.annotation.PostConstruct;

import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.target.AbstractBeanFactoryBasedTargetSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
Expand Down Expand Up @@ -57,6 +61,7 @@
import org.springframework.security.oauth2.provider.token.ConsumerTokenServices;
import org.springframework.security.oauth2.provider.token.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Component;

/**
* @author Rob Winch
Expand Down Expand Up @@ -283,4 +288,38 @@ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) t

}

/**
* If the user inadvertently autowires one of the lazy beans and then injects it back into the configurer they are
* going to create a proxy with a cyclic target. This processor ensures that at least there won't be a
* StackOverflowError (although the IllegalStateException comes too late to fail fast on startup). See
* https://jira.spring.io/browse/SPR-11684
*
* @author Dave Syer
*
*/
@Component
protected static class CyclicProxyDetector implements BeanPostProcessor {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Advised) {
Advised advised = (Advised) bean;
TargetSource targetSource = advised.getTargetSource();
if (targetSource != null && targetSource instanceof AbstractBeanFactoryBasedTargetSource) {
AbstractBeanFactoryBasedTargetSource source = (AbstractBeanFactoryBasedTargetSource) targetSource;
if (beanName.equals(source.getTargetBeanName())) {
throw new IllegalStateException("Cyclic proxy references itself as target: " + beanName);
}
}
}
return bean;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public class AuthorizationServerConfigurationTests {
public static List<Object[]> parameters() {
return Arrays.asList( // @formatter:off
new Object[] { BeanCreationException.class, new Class<?>[] { AuthorizationServerUnconfigured.class } },
new Object[] { BeanCreationException.class, new Class<?>[] { AuthorizationServerCycle.class } },
new Object[] { null, new Class<?>[] { AuthorizationServerVanilla.class } },
new Object[] { null, new Class<?>[] { AuthorizationServerDisableApproval.class } },
new Object[] { null, new Class<?>[] { AuthorizationServerExtras.class } },
Expand Down Expand Up @@ -155,6 +156,34 @@ public void run() {
}
}

@Configuration
@EnableWebMvcSecurity
@EnableAuthorizationServer
protected static class AuthorizationServerCycle extends AuthorizationServerConfigurerAdapter implements Runnable {
@Autowired
private AuthorizationServerTokenServices tokenServices;

@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenServices(tokenServices); // Bang! (Cyclic proxy)
}

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// @formatter:off
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("password");
// @formatter:on
}

@Override
public void run() {
tokenServices.createAccessToken(null);
}

}

@Configuration
@EnableWebMvcSecurity
@EnableAuthorizationServer
Expand All @@ -169,10 +198,7 @@ public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// @formatter:off
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.accessTokenValiditySeconds(60);
.authorizedGrantTypes("password");
// @formatter:on
}

Expand Down Expand Up @@ -250,10 +276,7 @@ public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// @formatter:off
clients.jdbc(dataSource())
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.accessTokenValiditySeconds(60);
.authorizedGrantTypes("password");
// @formatter:on
}

Expand Down Expand Up @@ -304,10 +327,7 @@ public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// @formatter:off
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.accessTokenValiditySeconds(60);
.authorizedGrantTypes("password");
// @formatter:on
}

Expand All @@ -333,10 +353,7 @@ public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// @formatter:off
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.accessTokenValiditySeconds(60);
.authorizedGrantTypes("password");
// @formatter:on
}

Expand Down

0 comments on commit d58d503

Please sign in to comment.