在 Spring Security 3.2.5 中,是什么导致 AuthenticationManager 实现内部出现无限循环?

Dus*_*tin 5 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,我很快发现这个配置会导致我的AuthenticationProviderImpl(实现 Spring SecurityAuthenticationProvider接口)出现问题。当实现的重写authenticate方法抛出 a时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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

mik*_*ika 5

几周前,我也重现了此行为,请参阅 stackoverflow 上的此线程

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

您在服务中的java.util.List顺序AuthenticationManager。该顺序由您的配置给出,例如通过执行您最初尝试的操作:

// Authentication provider
.and()
.authenticationProvider(authenticationProvider)
Run Code Online (Sandbox Code Playgroud)

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