5

I'm trying to configure my webSecurity to use both ldap and basic authentication (jdbc) with the new component-based security configuration (no WebSecurityConfigurerAdapter) but I can't get it to use both.

The required result is for spring to first attempt ldap, and if it doesn't find (or just fails for now is good enough) attempt to login using basic autentication.

The project is a migration from an older Spring-Boot version and with WebSecurityConfigurerAdapter the following code is what worked:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
        http.authorizeRequests().antMatchers("/services/**").permitAll().anyRequest().authenticated();
        http.httpBasic();
        http.formLogin().permitAll().loginPage("/login").defaultSuccessUrl("/customer/overview", true);
        http.logout().permitAll();

        http.csrf().disable();
        http.headers().frameOptions().disable();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception
    {
        auth.userDetailsService(userDetails);

        //@formatter:off
        auth.ldapAuthentication()
            .userSearchFilter("(uid={0})")
            .userSearchBase("ou=people")
            .groupSearchFilter("(uniqueMember={0})")
            .groupSearchBase("ou=groups")
            .groupRoleAttribute("cn")
            .rolePrefix("ROLE_")
            .userDetailsContextMapper(customLdapUserDetailsContextMapper())
            .contextSource()
            .url(ldapUrl);
        //@formatter:on
    }

    @Bean
    CustomLdapUserDetailsContextMapper customLdapUserDetailsContextMapper()
    {
        CustomLdapUserDetailsContextMapper mapper = new CustomLdapUserDetailsContextMapper();
        mapper.setCustomUserDetailsService(userDetailsService());

        return mapper;
    }
    //Implementation of custom contextMapper is not relevant for example i believe, basicly it maps some ldap roles, but for testing i don't use roles yet
}

and this is what my conversion to the new style looks like:

@Configuration
public class WebSecurityConfig
{
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager ldapAuthenticationManager) throws Exception
    {

        // @formatter:off
        http.authorizeRequests()
            .mvcMatchers("/services/**").permitAll()
            .mvcMatchers("/resources/**").permitAll()
            .mvcMatchers("/webjars/**").permitAll()
            .anyRequest().authenticated();
        http.httpBasic();
        http.formLogin().permitAll().loginPage("/login").defaultSuccessUrl("/customer/overview", true);
        http.logout().permitAll();

        http.csrf().disable();

        http.authenticationManager(ldapAuthenticationManager); //THIS LINE SEEMS TO BE PROBLEMATIC
        // @formatter:on

        return http.build();
    }


    @Bean
    public AuthenticationManager ldapAuthenticationManager(BaseLdapPathContextSource ldapContextSource, UserDetailsService userDetailsService)
    {
        LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(ldapContextSource);

        UserDetailsServiceLdapAuthoritiesPopulator ldapAuthoritiesPopulator = new UserDetailsServiceLdapAuthoritiesPopulator(userDetailsService);

        factory.setUserSearchFilter("(uid={0})");
        factory.setUserSearchBase("ou=people");
        factory.setLdapAuthoritiesPopulator(ldapAuthoritiesPopulator);

        return factory.createAuthenticationManager();
    }
}

when in the above new code the line http.authenticationManager(ldapAuthenticationManager); is enabled ldap login works fine (and it even binds roles from database user), but basic login doesn't work. however when the line is disabled basic login works but ldap does not.

Any help on how to get spring to use both logins would be much appreciated.

Ralan
  • 651
  • 6
  • 17

1 Answers1

5

Instead of creating a custom AuthenticationManager, you can create the AuthenticationProvider that will be used for LDAP authentication.

You can configure the provider on HttpSecurity:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, LdapAuthenticator authenticator) throws Exception {
    // ...
    http.authenticationProvider(
            new LdapAuthenticationProvider(authenticator, ldapAuthoritiesPopulator));
    // ...
    return http.build();
}

@Bean
BindAuthenticator authenticator(BaseLdapPathContextSource contextSource) {
    BindAuthenticator authenticator = new BindAuthenticator(contextSource);
    authenticator.setUserSearch(
            new FilterBasedLdapUserSearch("ou=people", "(uid={0})", contextSource));
    return authenticator;
}
  • Thanks, changing from authenticationManager to authenticationProvider did the trick, i did have to add a DaoAuthenticationProvider aswel since basic authentication did not work without but that was not alot of work. – Ralan Jun 09 '22 at 10:28
  • Glad to hear @Ralan. Although, if you have configured `http.httpBasic()` and `http.formLogin()`, I wouldn't expect you have to add `DaoAuthenticationProvider` manually. – Eleftheria Stein-Kousathana Jun 09 '22 at 11:14
  • For potential future users that read this, the apparant reason for my need to add DaoAuthenticationProvider is that i defined the AuthenticationProvider as a loose bean. As long as one sticks to defining a BindAuthenticator bean and making the provider within the SecurityFilterChain everything works as it should. – Ralan Jul 14 '22 at 09:33