在Spring Security 3.2.5中,什么导致AuthenticationManager实现中的无限循环?

时间:2015-02-24 01:24:39

标签: spring spring-mvc spring-security

不久前我遇到了一个有趣的情况,导致Spring Security' AuthenticationManager中出现无限循环(最终导致堆栈溢出)。几个月来,一切都按预期工作,但后来我决定将我的XML配置转移到仅代码配置。这是我在Java配置中的基本设置:

@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();
    }
}

但是,我很快发现这个配置会导致我的AuthenticationProviderImpl(实现Spring Security AuthenticationProvider接口)出现问题。当实现的重写authenticate方法抛出BadCredentialsException时,该类中的完全相同的方法将永久再次调用,直到堆栈溢出。好消息是,我通过简单地覆盖configure(AuthenticationManagerBuilder builder)中的SecurityConfig并声明我在AuthenticationProvider的实施而不是configure(HttpSecurity http)来修复我的配置。这是固定版本:

@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();
    }
}

虽然我相信我的问题已通过固定配置解决,但我仍然不知道为什么当我的authenticate()实现抛出异常时应用程序无限调用AuthenticationProvider?我试图单步执行并检查Spring Security类,但我没有找到合乎逻辑的答案。感谢您的专业知识!

1 个答案:

答案 0 :(得分:1)

几周前,我也重现了这种行为,see this thread on stackoverflow

处理这个问题我发现当AuthenticationManager在内部迭代它的关联AuthenticationProviders列表时会发生循环,然后找到一个自定义提供程序并尝试使用已提供的提供程序进行身份验证找到。如果提供程序通过调用AuthenticationManager将身份验证委托回authenticate(),则您处于循环中。我猜你的AuthenticationProviderImpl会做那样的事情吗?

java.util.List的{​​{1}}内的提供商的顺序很重要。订单由您的配置提供,例如通过你最初尝试的做法:

AuthenticationManager

通过更改您的配置,您影响了附加到您的经理的内部管理的提供商列表,最终将解决您的代码。