javaspringspring-bootldap

Spring Boot: Merging LDAP and Local Authentication with Conditional Form Login


I am developing a Spring Boot application that requires interfacing with an LDAP server for Role-Based Access Control (RBAC) authentication. Additionally, when the LDAP server is unavailable, the application should switch to using local backup accounts for authentication.

I have successfully implemented both authentication methods separately, and they work fine. However, I am encountering issues while merging the two implementations, particularly in the SecurityConfiguration class for form login.

Here’s the context of my implementation:

HTML Forms:

In login.html, I have the following form: html <form th:action="@{/user_login}" method="post"> In loginLdap.html, I have: html <form th:action="@{/admin_login}" method="post">

Controller Logic: I have a controller method that determines which login page to display based on LDAP availability:

@GetMapping("/login")
    public String login(Model model) {
        if (ldapService.isLdapAvailable()) {
            System.out.println("Inside login method. LDAP is available.");
            return "loginLdap";  // Redirect to loginLdap page if LDAP connection is successful
        } else {
            System.out.println("Inside login method. LDAP is unavailable.");
            model.addAttribute("ldapUnavailableMessage", "LDAP service is currently unavailable. Do you want to continue with local backup authentication?");
            return "login";  // Return login page without an LDAP           
            }
        }

Till this it is working fine. It is redirecting to the proper HTML page and I can see the login form based on LDAP availability. Now for

Security Configuration

I am having difficulty configuring the SecurityConfiguration class to handle both authentication methods correctly. Specifically, I am unsure how to set up the formLogin() method to accommodate the two different login forms based on the LDAP availability.

Now in the SecurityConfiguration class file I have following code :

 .formLogin(
                        form -> form
                                .loginPage("/login").loginProcessingUrl("/user_login").usernameParameter("username").passwordParameter("password").defaultSuccessUrl("/syslogger")
                                .permitAll()
                )

This code is working fine when LDAP is unavailable . All authorisation ( based on role in postgres db) is working fine. However when LDAP server is available it is not redirecting to the @PostMapping("/admin_login") in my main controller.

Issues: I am not sure how to configure the SecurityConfiguration class to handle these different login scenarios. I would like guidance on how to ensure that the correct authentication method is invoked based on the user's choice of login page.

Below is what I tried :

spring-security-two-login-pages

 @Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/public/**").permitAll()
            .antMatchers("/user/**").hasRole("USER")
            .antMatchers("/admin/**").hasRole("ADMIN")
            .anyRequest().authenticated()
            .and()
        .formLogin()
            .loginPage("/user-login")
            .loginProcessingUrl("/user-login-process")
            .defaultSuccessUrl("/user/home")
            .permitAll()
            .and()
        .formLogin()
            .loginPage("/admin-login")
            .loginProcessingUrl("/admin-login-process")
            .defaultSuccessUrl("/admin/dashboard")
            .permitAll()
            .and()
        .logout()
            .logoutSuccessUrl("/login")
            .permitAll();
}

Apart from these, various other techniques and logic I tried but not able to achieve the desired result.

Additional Information: I am using Spring Boot, REST API, and PostgreSQL for my application. Any help or examples regarding the SecurityConfiguration setup would be greatly appreciated!


Solution

  • After some experimentation and troubleshooting, I’m happy to share that I’ve found a solution that allows for dynamic switching between the two authentication methods based on the availability of the LDAP server.

    To handle both LDAP and local authentication based on the availability of the LDAP server, I modified my SecurityConfiguration class to dynamically configure the authentication method based on the LDAP service's status.

    Here's how I structured my SecurityConfiguration class:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
    
    import com.alstom.tet.syslog.service.LdapService;
    import com.alstom.tet.syslog.service.UserService;
    
    @Configuration
    @EnableWebSecurity
    public class SecurityConfiguration{
    
        @Autowired
        private UserService userService;
        
        @Autowired
        private LdapService ldapService;
        
            @Bean
            public static BCryptPasswordEncoder passwordEncoder() {
               return new BCryptPasswordEncoder();
            }
            @Bean
            public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
                if (ldapService.isLdapAvailable()) {
                    configureLdapAuthentication(http);
                } else {
                    configureLocalAuthentication(http);
                }
                return http.build();
            }
    

    Now implement the method configureLdapAuthentication as per your need.

    private void configureLdapAuthentication(HttpSecurity http) throws Exception {
    
    }
    

    similarly, implement the configureLocalAuthentication method according to your requirements.

     private void configureLocalAuthentication(HttpSecurity http) throws Exception {}
    

    The configure(HttpSecurity http) method checks if the LDAP service is available. If it is, it calls configureLdapAuthentication(http), otherwise it calls configureLocalAuthentication(http).

    This approach allows my Spring Boot application to seamlessly switch between LDAP and local authentication based on the availability of the LDAP server.

    Thanks everyone who tried to look at my question and tried to help.

    Finally my code is working as expected..