Click here to Skip to main content
15,867,488 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more: , +
I want to do LDAP authentication using Spring Boot, but I can't. I just want to check the person who wrote his username and password on our ldap server when he wants to log in. If we have such a user on our ldap server, I want it to rotate something successfully.Otherwise I want it to return the wrong password and either password or connection error. Seriously, it took me a while to figure it out. I've looked at all the sources, I've watched video lessons, but they're all reading from the pre-existing ldif file. But I just want to check the username and password entered from our own LDAP server using Spring Boot, not from any existing file.

so I found new tutorial topics, which there is using ldap test server, what I want to do. I have successfully login when I type username and password of their given.


application.properties file    
ldap.enabled = true

####### LDAP ##############
ldap.urls= ldap://ldap.forumsys.com:389/
ldap.base.dn= dc=example,dc=com
ldap.username= cn=read-only-admin,dc=example,dc=com
ldap.password= password
ldap.user.dn.pattern = uid={0}


server.port=8999
spring.main.banner-mode=off
spring.thymeleaf.cache=false


and this is my Security configuration.

import org.springframework.beans.factory.annotation.Value;
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.WebSecurityConfigurerAdapter;

@Configuration
public class SecurityConfig  extends WebSecurityConfigurerAdapter{

    @Value("${ldap.urls}")
    private String ldapUrls;

    @Value("${ldap.base.dn}")
    private String ldapBaseDn;

    @Value("${ldap.username}")
    private String ldapSecurityPrincipal;

    @Value("${ldap.password}")
    private String ldapPrincipalPassword;

    @Value("${ldap.user.dn.pattern}")
    private String ldapUserDnPattern;

    @Value("${ldap.enabled}")
    private String ldapEnabled;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/login**").permitAll()
                .antMatchers("/profile/**").fullyAuthenticated()
                .antMatchers("/").permitAll()
                .and()
                .formLogin()
                .loginPage("/login")
                .failureUrl("/login?error")
                .permitAll()
                .and()
                .logout()
                .invalidateHttpSession(true)
                .deleteCookies("JSESSIONID")
                .permitAll();
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        if(Boolean.parseBoolean(ldapEnabled)) {
            auth
                    .ldapAuthentication()
                    .contextSource()
                    .url(ldapUrls + ldapBaseDn)
                    .managerDn(ldapSecurityPrincipal)
                    .managerPassword(ldapPrincipalPassword)
                    .and()
                    .userDnPatterns(ldapUserDnPattern);
        } else {
            auth
                    .inMemoryAuthentication()
                    .withUser("user").password("password").roles("USER")
                    .and()
                    .withUser("admin").password("admin").roles("ADMIN");
        }
    }

}



But When I change url , base-dn, username and password and When I give our own values I am getting this below error:

Caused by: org.springframework.ldap.CommunicationException: myldapserverurl:389; nested exception is javax.naming.CommunicationException: myldapserverurl:389 [Root exception is java.net.ConnectException: Connection timed out: connect]


Caused by: javax.naming.CommunicationException: myldapserverurl:389


Caused by: java.net.ConnectException: Connection timed out: connect



Afterwards I thought that my application.properties file doesn't need the password information because in our ldap server each user has their own password. So I delete ldap.password key and do comment

@Value("${ldap.password}")
private String ldapPrincipalPassword;



and this line. .managerPassword(ldapPrincipalPassword)

Now when I run the program again I get the following error.

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is java.lang.IllegalStateException: managerPassword is required if managerDn is supplied


Caused by: java.lang.IllegalStateException: managerPassword is required if managerDn is supplied


In short, what I want from you. I want to perform my LDAP operations using Spring Boot without ldif file. can someone helps me about this topic?What should I change?

What I have tried:

I tried to many ways but still I couldn't
Posted
Updated 4-Apr-22 10:50am

1 solution

Input such as

username: user.name@domain.com
password: blablabla


#LDAP Properties
prefix.ldapIsEnabled=true
prefix.ldap_server_url=ldap://10.0.20.20:389
prefix.ldap_domain_name=domain.com
prefix.ldap_security_authentication= simple
prefix.ldap_users_search_base=ou=customGroup,dc=dmoain,dc=com


@ConfigurationProperties(prefix = "prefix")
@Component
public class Properties {

    private String ldapServerUrl;
    private String ldapDomainName;
    private String ldapSecurityAuthentication;
    private String ldapUsersSearchBase;
    private boolean ldapIsEnabled;

    public String getLdapSecurityAuthentication() {
        return ldapSecurityAuthentication;
    }

    public void setLdapSecurityAuthentication(String ldapSecurityAuthentication) {
        this.ldapSecurityAuthentication = ldapSecurityAuthentication;
    }

    public String getLdapUsersSearchBase() {
        return ldapUsersSearchBase;
    }

    public void setLdapUsersSearchBase(String ldapUsersSearchBase) {
        this.ldapUsersSearchBase = ldapUsersSearchBase;
    }

    public String getLdapDomainName() {
        return ldapDomainName;
    }

    public void setLdapDomainName(String ldapDomainName) {
        this.ldapDomainName = ldapDomainName;
    }

    public String getLdapServerUrl() {
        return ldapServerUrl;
    }

    public void setLdapServerUrl(String ldapServerUrl) {
        this.ldapServerUrl = ldapServerUrl;
    }

    public boolean isLdapIsEnabled() {
        return ldapIsEnabled;
    }

    public void setLdapIsEnabled(boolean ldapIsEnabled) {
        this.ldapIsEnabled = ldapIsEnabled;
    }

}



public class LoginRequest implements Serializable {

    private String email;
    private String password;

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

}




import java.util.Hashtable;
import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import org.springframework.stereotype.Service;

@Service
public class LDAPService {

    private final Properties props;

    public LDAPService(Properties props) {
        this.props = props;
    }

    public void validateUserCredentials(LoginRequest user) throws CustomException {

        if (props.isLdapIsEnabled()) {
            try {
                Hashtable<String, String> environment = new Hashtable<>();
                environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
                environment.put(Context.PROVIDER_URL, props.getLdapServerUrl());
                //environment.put(Context.SECURITY_PROTOCOL, "SSL");
                environment.put(Context.SECURITY_AUTHENTICATION, props.getLdapSecurityAuthentication());
                environment.put(Context.SECURITY_PRINCIPAL, createBindPrincipal(user.getEmail()));
                //environment.put(Context.SECURITY_PRINCIPAL, "cn=" + user.getUsername() + "," + props.getLdapUsersSearchBase());
                environment.put(Context.SECURITY_CREDENTIALS, user.getPassword());
                DirContext init = new InitialDirContext(environment);
            } catch (AuthenticationException ex) {
                //LDAP Status Code=49	Invalid credentials	AuthenticationException
                PeLogger.error("Authentication for user " + user.getEmail() + " FAILED", ex);
                PeLogger.error("Invalid credentials for user " + user.getEmail());
                throw new CustomException(StatusCodes.Error.INCORRECT_CREDENTIALS);
            } catch (NamingException ex) {
                PeLogger.error("Error when contacting LDAP server {}", props.getLdapServerUrl(), ex);
                PeLogger.error("Unable to connect to LDAP Server; check LDAP configuration", ex);
                throw new CustomException(StatusCodes.Error.CANNOT_AUTH_NOW);
            } catch (Exception ex) {
                PeLogger.error(ex);
                throw new CustomException(StatusCodes.Error.CANNOT_AUTH_NOW);
            }
        }
    }

    private String createBindPrincipal(String username) {
        if (username.toLowerCase().endsWith(props.getLdapDomainName().toLowerCase())) {
            return username;
        } else {
            return username + "@" + props.getLdapDomainName();
        }
    }
}


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private final String[] AUTH_WHITELIST = {
        // -- Login
        Defines.ContextPaths.LOGIN_URL,
        "/swagger-ui/**",
        "/swagger-ui*/**",
        "/swagger-ui/",
        "/swagger-ui.html",
        "/swagger-ui/index.html",
        "/api-docs",
        "/v3/api-docs"
    };

    @Bean
    public JwtAuthenticationFilter authenticationTokenFilterBean() throws Exception {
        return new JwtAuthenticationFilter();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable().
                authorizeRequests().antMatchers(AUTH_WHITELIST).permitAll()
                .anyRequest().authenticated()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
                and().
                addFilterBefore(authenticationTokenFilterBean(), AbstractPreAuthenticatedProcessingFilter.class);
    }
}
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900