Overview
LDAP (Lightweight Directory Access Protocol) is a protocol for accessing and maintaining distributed directory information services. In Java, LDAP authentication is commonly used for enterprise authentication against directory services like Active Directory, OpenLDAP, etc.
Core Java LDAP Classes
javax.naming.directory.InitialDirContextjavax.naming.directory.DirContextjavax.naming.AuthenticationExceptionjavax.naming.NamingException
Basic Authentication
1. Simple LDAP Authentication
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.DirContext;
import java.util.Hashtable;
public class SimpleLdapAuthenticator {
private static final String LDAP_URL = "ldap://localhost:389";
private static final String BASE_DN = "dc=example,dc=com";
public boolean authenticate(String username, String password) {
if (password == null || password.isEmpty()) {
return false;
}
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, LDAP_URL);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, createUserDn(username));
env.put(Context.SECURITY_CREDENTIALS, password);
try {
DirContext context = new InitialDirContext(env);
context.close();
return true;
} catch (AuthenticationException e) {
System.err.println("Authentication failed for user: " + username);
return false;
} catch (NamingException e) {
System.err.println("LDAP error: " + e.getMessage());
return false;
}
}
private String createUserDn(String username) {
return "uid=" + username + ",ou=users," + BASE_DN;
}
public static void main(String[] args) {
SimpleLdapAuthenticator authenticator = new SimpleLdapAuthenticator();
boolean result = authenticator.authenticate("john.doe", "password123");
System.out.println("Authentication result: " + result);
}
}
2. Configurable LDAP Authenticator
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.*;
import java.util.*;
public class LdapAuthenticator {
private final String ldapUrl;
private final String baseDn;
private final String userDnPattern;
private final String userSearchFilter;
public LdapAuthenticator(String ldapUrl, String baseDn, String userDnPattern, String userSearchFilter) {
this.ldapUrl = ldapUrl;
this.baseDn = baseDn;
this.userDnPattern = userDnPattern;
this.userSearchFilter = userSearchFilter;
}
public AuthenticationResult authenticate(String username, String password) {
if (password == null || password.isEmpty()) {
return AuthenticationResult.failure("Password cannot be empty");
}
try {
// First, search for user DN if using search filter
String userDn = findUserDn(username);
if (userDn == null) {
return AuthenticationResult.failure("User not found");
}
// Attempt authentication
return attemptAuthentication(userDn, password);
} catch (NamingException e) {
return AuthenticationResult.failure("LDAP error: " + e.getMessage());
}
}
private String findUserDn(String username) throws NamingException {
if (userDnPattern != null) {
// Use DN pattern
return userDnPattern.replace("{0}", username);
} else if (userSearchFilter != null) {
// Search for user DN
return searchUserDn(username);
}
throw new IllegalStateException("Either userDnPattern or userSearchFilter must be configured");
}
private String searchUserDn(String username) throws NamingException {
Hashtable<String, String> env = createEnvironment(null, null); // Anonymous bind for search
DirContext context = new InitialDirContext(env);
try {
String filter = userSearchFilter.replace("{0}", username);
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
controls.setReturningAttributes(new String[]{"dn"});
NamingEnumeration<SearchResult> results = context.search(baseDn, filter, controls);
if (results.hasMore()) {
SearchResult result = results.next();
return result.getNameInNamespace();
}
return null;
} finally {
context.close();
}
}
private AuthenticationResult attemptAuthentication(String userDn, String password) {
Hashtable<String, String> env = createEnvironment(userDn, password);
try {
DirContext context = new InitialDirContext(env);
// Authentication successful - get user attributes
UserInfo userInfo = getUserInfo(context, userDn);
context.close();
return AuthenticationResult.success(userInfo);
} catch (AuthenticationException e) {
return AuthenticationResult.failure("Invalid credentials");
} catch (NamingException e) {
return AuthenticationResult.failure("LDAP error: " + e.getMessage());
}
}
private UserInfo getUserInfo(DirContext context, String userDn) throws NamingException {
Attributes attributes = context.getAttributes(userDn);
return UserInfo.fromAttributes(attributes, userDn);
}
private Hashtable<String, String> createEnvironment(String userDn, String password) {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapUrl);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
if (userDn != null && password != null) {
env.put(Context.SECURITY_PRINCIPAL, userDn);
env.put(Context.SECURITY_CREDENTIALS, password);
}
// Additional configuration
env.put(Context.REFERRAL, "follow");
env.put("com.sun.jndi.ldap.connect.timeout", "5000");
env.put("com.sun.jndi.ldap.read.timeout", "5000");
return env;
}
// Builder pattern for configuration
public static class Builder {
private String ldapUrl;
private String baseDn;
private String userDnPattern;
private String userSearchFilter = "(uid={0})";
public Builder withLdapUrl(String ldapUrl) {
this.ldapUrl = ldapUrl;
return this;
}
public Builder withBaseDn(String baseDn) {
this.baseDn = baseDn;
return this;
}
public Builder withUserDnPattern(String userDnPattern) {
this.userDnPattern = userDnPattern;
return this;
}
public Builder withUserSearchFilter(String userSearchFilter) {
this.userSearchFilter = userSearchFilter;
return this;
}
public LdapAuthenticator build() {
return new LdapAuthenticator(ldapUrl, baseDn, userDnPattern, userSearchFilter);
}
}
}
3. Authentication Result and User Info Classes
import javax.naming.directory.Attributes;
import javax.naming.directory.Attribute;
import java.util.*;
public class AuthenticationResult {
private final boolean success;
private final String errorMessage;
private final UserInfo userInfo;
private AuthenticationResult(boolean success, String errorMessage, UserInfo userInfo) {
this.success = success;
this.errorMessage = errorMessage;
this.userInfo = userInfo;
}
public static AuthenticationResult success(UserInfo userInfo) {
return new AuthenticationResult(true, null, userInfo);
}
public static AuthenticationResult failure(String errorMessage) {
return new AuthenticationResult(false, errorMessage, null);
}
// Getters
public boolean isSuccess() { return success; }
public String getErrorMessage() { return errorMessage; }
public UserInfo getUserInfo() { return userInfo; }
}
public class UserInfo {
private final String distinguishedName;
private final String username;
private final String email;
private final String firstName;
private final String lastName;
private final List<String> groups;
private final Map<String, Object> attributes;
private UserInfo(String distinguishedName, String username, String email,
String firstName, String lastName, List<String> groups,
Map<String, Object> attributes) {
this.distinguishedName = distinguishedName;
this.username = username;
this.email = email;
this.firstName = firstName;
this.lastName = lastName;
this.groups = groups;
this.attributes = attributes;
}
public static UserInfo fromAttributes(Attributes attributes, String dn) throws NamingException {
String username = getAttributeValue(attributes, "uid");
String email = getAttributeValue(attributes, "mail");
String firstName = getAttributeValue(attributes, "givenName");
String lastName = getAttributeValue(attributes, "sn");
Map<String, Object> attrMap = new HashMap<>();
NamingEnumeration<? extends Attribute> allAttributes = attributes.getAll();
while (allAttributes.hasMore()) {
Attribute attr = allAttributes.next();
String attrId = attr.getID();
if (attr.size() == 1) {
attrMap.put(attrId, attr.get());
} else {
List<Object> values = new ArrayList<>();
NamingEnumeration<?> valuesEnum = attr.getAll();
while (valuesEnum.hasMore()) {
values.add(valuesEnum.next());
}
attrMap.put(attrId, values);
}
}
return new UserInfo(dn, username, email, firstName, lastName,
Collections.emptyList(), attrMap);
}
private static String getAttributeValue(Attributes attributes, String attributeName)
throws NamingException {
Attribute attribute = attributes.get(attributeName);
return attribute != null ? (String) attribute.get() : null;
}
// Getters
public String getDistinguishedName() { return distinguishedName; }
public String getUsername() { return username; }
public String getEmail() { return email; }
public String getFirstName() { return firstName; }
public String getLastName() { return lastName; }
public List<String> getGroups() { return groups; }
public Map<String, Object> getAttributes() { return attributes; }
@Override
public String toString() {
return String.format("UserInfo[username=%s, email=%s, name=%s %s]",
username, email, firstName, lastName);
}
}
Advanced LDAP Operations
1. LDAP Connection Pool
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class LdapConnectionPool {
private final BlockingQueue<DirContext> connectionPool;
private final Hashtable<String, String> environment;
private final int maxConnections;
private int activeConnections = 0;
public LdapConnectionPool(String ldapUrl, String bindDn, String bindPassword, int poolSize) {
this.maxConnections = poolSize;
this.connectionPool = new LinkedBlockingQueue<>(poolSize);
this.environment = createEnvironment(ldapUrl, bindDn, bindPassword);
initializePool();
}
private void initializePool() {
for (int i = 0; i < Math.min(5, maxConnections); i++) {
try {
DirContext context = createConnection();
connectionPool.offer(context);
} catch (NamingException e) {
System.err.println("Failed to create initial connection: " + e.getMessage());
}
}
}
public DirContext getConnection() throws InterruptedException, NamingException {
DirContext context = connectionPool.poll(5, TimeUnit.SECONDS);
if (context != null) {
return context;
}
synchronized (this) {
if (activeConnections < maxConnections) {
activeConnections++;
return createConnection();
}
}
throw new RuntimeException("Connection pool exhausted");
}
public void returnConnection(DirContext context) {
if (context != null) {
if (!connectionPool.offer(context)) {
// Pool is full, close the connection
closeConnection(context);
synchronized (this) {
activeConnections--;
}
}
}
}
public void close() {
DirContext context;
while ((context = connectionPool.poll()) != null) {
closeConnection(context);
}
synchronized (this) {
activeConnections = 0;
}
}
private DirContext createConnection() throws NamingException {
return new InitialDirContext(environment);
}
private void closeConnection(DirContext context) {
try {
context.close();
} catch (NamingException e) {
System.err.println("Error closing LDAP connection: " + e.getMessage());
}
}
private Hashtable<String, String> createEnvironment(String ldapUrl, String bindDn, String bindPassword) {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapUrl);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, bindDn);
env.put(Context.SECURITY_CREDENTIALS, bindPassword);
env.put(Context.REFERRAL, "follow");
env.put("com.sun.jndi.ldap.connect.pool", "true");
env.put("com.sun.jndi.ldap.connect.timeout", "5000");
return env;
}
}
2. Group Membership Checking
import javax.naming.NamingException;
import javax.naming.directory.*;
import java.util.*;
public class LdapGroupService {
private final LdapConnectionPool connectionPool;
private final String baseDn;
private final String groupSearchBase;
public LdapGroupService(LdapConnectionPool connectionPool, String baseDn, String groupSearchBase) {
this.connectionPool = connectionPool;
this.baseDn = baseDn;
this.groupSearchBase = groupSearchBase;
}
public List<String> getUserGroups(String userDn) throws NamingException {
DirContext context = null;
try {
context = connectionPool.getConnection();
// Search for groups where user is a member
String filter = "(member=" + userDn + ")";
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
controls.setReturningAttributes(new String[]{"cn"});
List<String> groups = new ArrayList<>();
NamingEnumeration<SearchResult> results = context.search(groupSearchBase, filter, controls);
while (results.hasMore()) {
SearchResult result = results.next();
Attributes attributes = result.getAttributes();
Attribute cn = attributes.get("cn");
if (cn != null) {
groups.add((String) cn.get());
}
}
return groups;
} finally {
if (context != null) {
connectionPool.returnConnection(context);
}
}
}
public boolean isUserInGroup(String userDn, String groupName) throws NamingException {
DirContext context = null;
try {
context = connectionPool.getConnection();
// Search for specific group with user as member
String filter = "(&(cn=" + groupName + ")(member=" + userDn + "))";
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
controls.setReturningAttributes(new String[]{"cn"});
NamingEnumeration<SearchResult> results = context.search(groupSearchBase, filter, controls);
return results.hasMore();
} finally {
if (context != null) {
connectionPool.returnConnection(context);
}
}
}
public Map<String, List<String>> getGroupMembers(String groupName) throws NamingException {
DirContext context = null;
try {
context = connectionPool.getConnection();
String filter = "(cn=" + groupName + ")";
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
controls.setReturningAttributes(new String[]{"member"});
Map<String, List<String>> groupMembers = new HashMap<>();
NamingEnumeration<SearchResult> results = context.search(groupSearchBase, filter, controls);
if (results.hasMore()) {
SearchResult result = results.next();
Attributes attributes = result.getAttributes();
Attribute memberAttribute = attributes.get("member");
if (memberAttribute != null) {
List<String> members = new ArrayList<>();
NamingEnumeration<?> membersEnum = memberAttribute.getAll();
while (membersEnum.hasMore()) {
members.add((String) membersEnum.next());
}
groupMembers.put(groupName, members);
}
}
return groupMembers;
} finally {
if (context != null) {
connectionPool.returnConnection(context);
}
}
}
}
3. Spring Security LDAP Integration
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.ldap.query.LdapQuery;
import org.springframework.ldap.query.LdapQueryBuilder;
import org.springframework.ldap.support.LdapUtils;
import java.util.*;
public class SpringLdapAuthenticationProvider implements AuthenticationProvider {
private final LdapTemplate ldapTemplate;
private final String userDnPattern;
private final String userSearchBase;
private final String userSearchFilter;
public SpringLdapAuthenticationProvider(LdapContextSource contextSource,
String userDnPattern,
String userSearchBase,
String userSearchFilter) {
this.ldapTemplate = new LdapTemplate(contextSource);
this.userDnPattern = userDnPattern;
this.userSearchBase = userSearchBase;
this.userSearchFilter = userSearchFilter;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
try {
// Authenticate user
DirContextOperations userContext = authenticateUser(username, password);
// Get user authorities (roles)
List<GrantedAuthority> authorities = getUserAuthorities(userContext);
// Create successful authentication token
return new UsernamePasswordAuthenticationToken(
username, password, authorities);
} catch (org.springframework.ldap.AuthenticationException e) {
throw new BadCredentialsException("Invalid credentials", e);
} catch (Exception e) {
throw new AuthenticationServiceException("LDAP authentication failed", e);
}
}
private DirContextOperations authenticateUser(String username, String password) {
if (userDnPattern != null) {
// Use DN pattern authentication
String userDn = userDnPattern.replace("{0}", username);
return ldapTemplate.authenticate(
LdapQueryBuilder.query().base("").filter("(objectClass=*)"),
userDn, password);
} else {
// Use search-based authentication
return ldapTemplate.authenticate(
LdapQueryBuilder.query()
.base(userSearchBase)
.filter(userSearchFilter, username),
password);
}
}
private List<GrantedAuthority> getUserAuthorities(DirContextOperations userContext) {
List<GrantedAuthority> authorities = new ArrayList<>();
// Add default role
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
// Get groups from LDAP
String[] groups = userContext.getStringAttributes("memberOf");
if (groups != null) {
for (String groupDn : groups) {
String groupName = extractGroupName(groupDn);
if (groupName != null) {
authorities.add(new SimpleGrantedAuthority("ROLE_" + groupName.toUpperCase()));
}
}
}
return authorities;
}
private String extractGroupName(String groupDn) {
// Extract CN from DN (e.g., "CN=Admins,OU=Groups,DC=example,DC=com" -> "Admins")
if (groupDn != null && groupDn.startsWith("CN=")) {
int commaIndex = groupDn.indexOf(',');
if (commaIndex > 0) {
return groupDn.substring(3, commaIndex);
}
}
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
// Spring Configuration
@Configuration
@EnableWebSecurity
public class LdapSecurityConfig {
@Value("${ldap.url}")
private String ldapUrl;
@Value("${ldap.base.dn}")
private String baseDn;
@Value("${ldap.user.dn.pattern}")
private String userDnPattern;
@Bean
public LdapContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(ldapUrl);
contextSource.setBase(baseDn);
contextSource.setUserDn("cn=admin," + baseDn);
contextSource.setPassword("adminPassword");
return contextSource;
}
@Bean
public AuthenticationProvider ldapAuthenticationProvider() {
return new SpringLdapAuthenticationProvider(
contextSource(),
userDnPattern,
"ou=users",
"(uid={0})"
);
}
}
Complete Example with Error Handling
import javax.naming.*;
import javax.naming.directory.*;
import java.util.*;
public class ComprehensiveLdapService {
private final LdapConfig config;
private final LdapConnectionPool connectionPool;
public ComprehensiveLdapService(LdapConfig config) {
this.config = config;
this.connectionPool = new LdapConnectionPool(
config.getLdapUrl(),
config.getBindDn(),
config.getBindPassword(),
config.getPoolSize()
);
}
public AuthenticationResult authenticateUser(String username, String password) {
try {
// Step 1: Find user DN
String userDn = findUserDn(username);
if (userDn == null) {
return AuthenticationResult.failure("User not found");
}
// Step 2: Authenticate
UserInfo userInfo = authenticateAndGetUserInfo(userDn, password);
if (userInfo == null) {
return AuthenticationResult.failure("Authentication failed");
}
// Step 3: Get group membership
List<String> groups = getUserGroups(userDn);
UserInfo userWithGroups = userInfo.withGroups(groups);
return AuthenticationResult.success(userWithGroups);
} catch (NamingException e) {
return handleNamingException(e);
} catch (Exception e) {
return AuthenticationResult.failure("Unexpected error: " + e.getMessage());
}
}
private String findUserDn(String username) throws NamingException {
DirContext context = null;
try {
context = connectionPool.getConnection();
String filter = config.getUserSearchFilter().replace("{0}", username);
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
controls.setReturningAttributes(new String[]{"dn"});
NamingEnumeration<SearchResult> results = context.search(
config.getUserSearchBase(), filter, controls);
if (results.hasMore()) {
SearchResult result = results.next();
return result.getNameInNamespace();
}
return null;
} finally {
if (context != null) {
connectionPool.returnConnection(context);
}
}
}
private UserInfo authenticateAndGetUserInfo(String userDn, String password) {
Hashtable<String, String> env = createAuthenticationEnvironment(userDn, password);
try {
DirContext authContext = new InitialDirContext(env);
// Get user attributes
Attributes attributes = authContext.getAttributes(userDn);
UserInfo userInfo = UserInfo.fromAttributes(attributes, userDn);
authContext.close();
return userInfo;
} catch (AuthenticationException e) {
return null;
} catch (NamingException e) {
throw new RuntimeException("LDAP error during authentication", e);
}
}
private List<String> getUserGroups(String userDn) throws NamingException {
DirContext context = null;
try {
context = connectionPool.getConnection();
String filter = "(&(objectClass=groupOfNames)(member=" + userDn + "))";
SearchControls controls = new SearchControls();
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
controls.setReturningAttributes(new String[]{"cn"});
List<String> groups = new ArrayList<>();
NamingEnumeration<SearchResult> results = context.search(
config.getGroupSearchBase(), filter, controls);
while (results.hasMore()) {
SearchResult result = results.next();
Attributes attributes = result.getAttributes();
Attribute cn = attributes.get("cn");
if (cn != null) {
groups.add((String) cn.get());
}
}
return groups;
} finally {
if (context != null) {
connectionPool.returnConnection(context);
}
}
}
private AuthenticationResult handleNamingException(NamingException e) {
if (e instanceof AuthenticationException) {
return AuthenticationResult.failure("Invalid credentials");
} else if (e instanceof CommunicationException) {
return AuthenticationResult.failure("Cannot connect to LDAP server");
} else if (e instanceof NameNotFoundException) {
return AuthenticationResult.failure("User not found");
} else {
return AuthenticationResult.failure("LDAP error: " + e.getMessage());
}
}
private Hashtable<String, String> createAuthenticationEnvironment(String userDn, String password) {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, config.getLdapUrl());
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, userDn);
env.put(Context.SECURITY_CREDENTIALS, password);
env.put(Context.REFERRAL, "follow");
env.put("com.sun.jndi.ldap.connect.timeout", "5000");
env.put("com.sun.jndi.ldap.read.timeout", "5000");
return env;
}
public void close() {
connectionPool.close();
}
}
// Configuration class
public class LdapConfig {
private String ldapUrl;
private String baseDn;
private String bindDn;
private String bindPassword;
private String userSearchBase;
private String userSearchFilter = "(uid={0})";
private String groupSearchBase;
private int poolSize = 10;
// Getters and setters
public String getLdapUrl() { return ldapUrl; }
public void setLdapUrl(String ldapUrl) { this.ldapUrl = ldapUrl; }
public String getBaseDn() { return baseDn; }
public void setBaseDn(String baseDn) { this.baseDn = baseDn; }
public String getBindDn() { return bindDn; }
public void setBindDn(String bindDn) { this.bindDn = bindDn; }
public String getBindPassword() { return bindPassword; }
public void setBindPassword(String bindPassword) { this.bindPassword = bindPassword; }
public String getUserSearchBase() { return userSearchBase; }
public void setUserSearchBase(String userSearchBase) { this.userSearchBase = userSearchBase; }
public String getUserSearchFilter() { return userSearchFilter; }
public void setUserSearchFilter(String userSearchFilter) { this.userSearchFilter = userSearchFilter; }
public String getGroupSearchBase() { return groupSearchBase; }
public void setGroupSearchBase(String groupSearchBase) { this.groupSearchBase = groupSearchBase; }
public int getPoolSize() { return poolSize; }
public void setPoolSize(int poolSize) { this.poolSize = poolSize; }
}
Best Practices
- Use connection pooling for better performance
- Always close connections in finally blocks
- Handle different LDAP exceptions appropriately
- Use secure LDAP (LDAPS) in production
- Validate input to prevent LDAP injection
- Implement proper logging for debugging
- Use configuration files for LDAP settings
- Test with different directory servers (Active Directory, OpenLDAP, etc.)
Common LDAP Servers Configuration
Active Directory
LdapConfig adConfig = new LdapConfig();
adConfig.setLdapUrl("ldap://ad.company.com:389");
adConfig.setBaseDn("DC=company,DC=com");
adConfig.setUserSearchBase("CN=Users,DC=company,DC=com");
adConfig.setUserSearchFilter("(&(objectClass=user)(sAMAccountName={0}))");
adConfig.setGroupSearchBase("CN=Users,DC=company,DC=com");
OpenLDAP
LdapConfig openLdapConfig = new LdapConfig();
openLdapConfig.setLdapUrl("ldap://openldap.company.com:389");
openLdapConfig.setBaseDn("dc=company,dc=com");
openLdapConfig.setUserSearchBase("ou=users,dc=company,dc=com");
adConfig.setUserSearchFilter("(uid={0})");
adConfig.setGroupSearchBase("ou=groups,dc=company,dc=com");
This comprehensive LDAP authentication implementation provides robust error handling, connection pooling, and support for various directory services while maintaining security and performance best practices.