In Spring Security 3.2.5, what is causing an infinite loop inside the AuthenticationManager implementation?

spring, spring-mvc, spring-security

Solution

A few weeks ago I reproduced this behavior, too, see this thread on stackoverflow.

Dealing with the question I figured out that loops occur when the `AuthenticationManager` internally iterates through it's list of associated `AuthenticationProviders`, then finds a custom provider and tries to do the authentication using the provider that has been found. If the provider delegates the authentication back to the `AuthenticationManager` by calling `authenticate()`, you are in the loop. I guess your `AuthenticationProviderImpl` does something like that?

The order of your in the providers inside the `java.util.List` of the `AuthenticationManager` matters. The order is given by your configuration, e.g. by doing what you tried at first:

// Authentication provider
.and()
.authenticationProvider(authenticationProvider)

By changing your configuration, you influenced the internally managed list of providers attached to your manager, which in the end will solve your code.

Problem

I had an interesting situation not long ago which caused an infinite loop (and eventually a stack overflow) in Spring Security's `AuthenticationManager`. For months, everything worked as expected, but then I decided to transfer my XML configuration to code-only configuration. Here was my basic setup in Java configuration: ``` @Configuration @EnableWebMvcSecurity @ComponentScan(basePackages = { "com.my.company" }) public class SecurityConfig extends WebSecurityConfigurerAdapter { // Disable default configuration public SecurityConfig() { super(true); } @Autowired AuthenticationProviderImpl authenticationProvider; @Autowired MyAuthenticationEntryPoint customAuthenticationEntryPoint; @Autowired AuthenticationTokenProcessingFilter authenticationTokenProcessingFilter; @Bean(name = "authenticationManager") @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override public void configure(WebSecurity web) throws Exception { // Ignore requests of resources in security web.ignoring().antMatchers("/resources/**") // Ignore requests to authentication .and().ignoring().antMatchers("/auth/**"); } @Override protected void configure(HttpSecurity http) throws Exception { // Define main authentication filter http.addFilterBefore(authenticationTokenProcessingFilter, UsernamePasswordAuthenticationFilter.class) // Request path authorization .authorizeRequests() .antMatchers("/api/**") .access("isAuthenticated()") // Authentication provider .and() .authenticationProvider(authenticationProvider) // Security failure exception handling .exceptionHandling() .authenticationEntryPoint(customAuthenticationEntryPoint) // Session Management .and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // Default security HTTP headers .and().headers().xssProtection().frameOptions() .cacheControl().contentTypeOptions(); } } ``` However, I soon found out that this configuration causes issues with my `AuthenticationProviderImpl` (which implements the Spring Security `AuthenticationProvider` interface). When the implementation's overridden `authenticate` method throws a `BadCredentialsException`, the exact same method in that class is called again perpetually until the stack overflows. The good news is that I fixed my configuration by simply overriding `configure(AuthenticationManagerBuilder builder)` in the `SecurityConfig` and declaring my implementation of the `AuthenticationProvider` there instead of in `configure(HttpSecurity http)`. Here is the fixed version: ``` @Configuration @EnableWebMvcSecurity @ComponentScan(basePackages = { "com.my.company" }) public class SecurityConfig extends WebSecurityConfigurerAdapter { // Disable default configuration public SecurityConfig() { super(true); } @Autowired AuthenticationProviderImpl authenticationProvider; @Autowired MyAuthenticationEntryPoint customAuthenticationEntryPoint; @Autowired AuthenticationTokenProcessingFilter authenticationTokenProcessingFilter; @Bean(name = "authenticationManager") @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override public void configure(AuthenticationManagerBuilder builder) { // Configure the authentication manager WITH the authentication // provider. Not overriding this method causes very bad things to // happen. builder.authenticationProvider(authenticationProvider); } @Override public void configure(WebSecurity web) throws Exception { // Ignore requests of resources in security web.ignoring().antMatchers("/resources/**") // Ignore requests to authentication .and().ignoring().antMatchers("/auth/**"); } @Override protected void configure(HttpSecurity http) throws Exception { // Define main authentication filter http.addFilterBefore(authenticationTokenProcessingFilter, UsernamePasswordAuthenticationFilter.class) // Request path authorization .authorizeRequests() .antMatchers("/api/**") .access("isAuthenticated()") .and() // Security failure exception handling .exceptionHandling() .authenticationEntryPoint(customAuthenticationEntryPoint) // Session Management .and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // Default security HTTP headers .and().headers().xssProtection().frameOptions() .cacheControl().contentTypeOptions(); } } ``` Though I believe my problem is solved with the fixed configuration, I still have no idea why the application was infinitely calling `authenticate()` when an exception was thrown by my implementation of `AuthenticationProvider`? I tried stepping through and examining the Spring Security classes, but I was not finding a logical answer. Thanks ahead for your expertise!

Original source

Related problems