Dimi Dimi - 1 month ago 46
Java Question

Using multiple WebSecurityConfigurerAdapter with different AuthenticationProviders (basic auth for API and LDAP for web app)

According the Spring Security Reference section 5.7 it should be possible to define more than one security adapter.

I try to do the same but without success. After a server reboot, the first x times the API works fine with basic auth, but after a couple of times I'm redirected to the login (form) page, this should only happen for our web app, not for the API calls.

My code:

@EnableWebSecurity
public class MultiHttpSecurityConfig {

@Configuration
@Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {

@Autowired
private Environment env;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().
withUser("admin").password("pw_test").roles(API_ROLE);
}

protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/services/**")
.authorizeRequests()
.anyRequest().hasRole(API_ROLE)
.and()
.httpBasic()
.and()
.csrf()
.disable();
}
}

@Configuration
@Order(2)
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

@Autowired
private Environment env;

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
auth.eraseCredentials(false);
}

@Override
protected void configure(HttpSecurity http) throws Exception {
// LDAP FORM AUTHENTICATION
http.authorizeRequests()
.antMatchers("/login.html").permitAll()
.antMatchers("/css/**").permitAll()
.antMatchers("/js/**").permitAll()
.antMatchers("/images/**").permitAll()
.anyRequest().authenticated()
.and().formLogin()
.failureUrl("/login.html?error=1")
.loginPage("/login.html")
.loginProcessingUrl("/j_spring_security_check")
.defaultSuccessUrl("/success.html")
.usernameParameter("j_username")
.passwordParameter("j_password")
.permitAll();

http.csrf().disable();

// iFRAMES SETTINGS
http
.headers()
.frameOptions().sameOrigin()
.httpStrictTransportSecurity().disable();

// HTTPS
http
.requiresChannel()
.anyRequest()
.requiresSecure();

//MAP 8080 to HTTPS PORT
http.portMapper().http(8080).mapsTo(443);
}

@Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
CustomLdapAuthenticationProvider provider = new CustomLdapAuthenticationProvider(env.getProperty("ldap.domain"), env.getProperty("ldap.url"), env.getProperty("ldap.base"));
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}
}


Any idea?

I'm using Spring Boot version 1.4.1-RELEASE and Spring Security version 4.1.3-RELEASE.

dur dur
Answer

You use the same AuthenticationProvider for both configurations, because you autowire the same AuthenticationManagerBuilder.

See Spring Security Architecture:

@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {

    ... // web stuff here

    @Autowired
    public initialize(AuthenticationManagerBuilder builder, DataSource dataSource) {
        auth.jdbcAuthentication().dataSource(dataSource).withUser("dave")
            .password("secret").roles("USER");
    }

}

This example relates to a web application, but the usage of AuthenticationManagerBuilder is more widely applicable (see below for more detail on how web application security is implemented). Note that the AuthenticationManagerBuilder is @Autowired into a method in a @Bean - that is what makes it build the global (parent) AuthenticationManager. In contrast if we had done it this way:

@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {

    @Autowired
    DataSource dataSource;

    ... // web stuff here

    @Override
    public configure(AuthenticationManagerBuilder builder) {
        auth.jdbcAuthentication().dataSource(dataSource).withUser("dave")
            .password("secret").roles("USER");
    }

}

(using an @Override of a method in the configurer) then the AuthenticationManagerBuilder is only used to build a "local" AuthenticationManager, which is a child of the global one.

Comments