Spring Security LDAP module providing comprehensive LDAP authentication and authorization capabilities for enterprise applications
—
Authority and role mapping from LDAP groups and attributes with customizable population strategies for complex authorization scenarios.
Strategy interface for populating user authorities from LDAP directory information.
/**
* Strategy interface for retrieving user authorities from LDAP directory entries
*/
public interface LdapAuthoritiesPopulator {
/**
* Retrieves the granted authorities for a user based on their LDAP entry
* @param userData the LDAP context operations containing user information
* @param username the username of the authenticated user
* @return collection of granted authorities for the user
*/
Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username);
}Default implementation that populates authorities from LDAP group memberships and user attributes.
/**
* Default LDAP authorities populator that retrieves user authorities from LDAP group memberships
* and optionally from user attributes
*/
public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator, InitializingBean, MessageSourceAware {
/**
* Creates an authorities populator with context source and group search base
* @param contextSource the LDAP context source
* @param groupSearchBase the base DN for searching groups (e.g., "ou=groups")
*/
public DefaultLdapAuthoritiesPopulator(ContextSource contextSource, String groupSearchBase);
/**
* Retrieves granted authorities from LDAP groups and user attributes
* @param userData the user's LDAP context operations
* @param username the username
* @return collection of granted authorities
*/
public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username);
/**
* Sets the LDAP filter for searching groups that contain the user
* @param groupSearchFilter filter with {0} placeholder for user DN (default: "member={0}")
*/
public void setGroupSearchFilter(String groupSearchFilter);
/**
* Sets the attribute name that contains the role/authority name in group entries
* @param groupRoleAttribute attribute name (default: "cn")
*/
public void setGroupRoleAttribute(String groupRoleAttribute);
/**
* Sets the prefix to add to role names extracted from groups
* @param rolePrefix prefix to add (default: "ROLE_")
*/
public void setRolePrefix(String rolePrefix);
/**
* Sets whether to convert role names to uppercase
* @param convertToUpperCase true to convert to uppercase (default: true)
*/
public void setConvertToUpperCase(boolean convertToUpperCase);
/**
* Sets the scope for group searches
* @param groupSearchSubtree true to search subtree, false for one level (default: false)
*/
public void setSearchSubtree(boolean groupSearchSubtree);
/**
* Sets a user attribute that should be used as an additional authority
* @param attributeName the name of the user attribute containing role information
*/
public void setGroupRoleAttribute(String attributeName);
/**
* Sets the default role to assign to all users
* @param defaultRole the default role name
*/
public void setDefaultRole(String defaultRole);
/**
* Sets whether to ignore partial result exceptions during group searches
* @param ignore true to ignore partial result exceptions
*/
public void setIgnorePartialResultException(boolean ignore);
}Usage Examples:
// Basic authorities populator
DefaultLdapAuthoritiesPopulator authoritiesPopulator =
new DefaultLdapAuthoritiesPopulator(contextSource, "ou=groups");
// Customized group search
authoritiesPopulator.setGroupSearchFilter("member={0}");
authoritiesPopulator.setGroupRoleAttribute("cn");
authoritiesPopulator.setRolePrefix("ROLE_");
authoritiesPopulator.setConvertToUpperCase(true);
// Search configuration
authoritiesPopulator.setSearchSubtree(true);
authoritiesPopulator.setIgnorePartialResultException(true);
// Default role for all users
authoritiesPopulator.setDefaultRole("USER");Authorities populator that delegates to a UserDetailsService for authority retrieval.
/**
* LdapAuthoritiesPopulator that obtains authorities from a UserDetailsService
*/
public class UserDetailsServiceLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
/**
* Creates a populator with the specified UserDetailsService
* @param userService the UserDetailsService to delegate to
*/
public UserDetailsServiceLdapAuthoritiesPopulator(UserDetailsService userService);
/**
* Gets authorities by loading UserDetails for the username
* @param userData the LDAP context operations (ignored)
* @param username the username to look up
* @return collection of authorities from UserDetailsService
*/
public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username);
}Authorities populator that can recursively search nested LDAP groups.
/**
* LDAP authorities populator that can recursively search static nested groups
*/
public class NestedLdapAuthoritiesPopulator extends DefaultLdapAuthoritiesPopulator {
/**
* Creates a nested authorities populator
* @param contextSource the LDAP context source
* @param groupSearchBase the base DN for group searches
*/
public NestedLdapAuthoritiesPopulator(ContextSource contextSource, String groupSearchBase);
/**
* Sets the maximum search depth for nested groups
* @param maxSearchDepth maximum depth (default: 10)
*/
public void setMaxSearchDepth(int maxSearchDepth);
/**
* Sets additional search filters for nested group searches
* @param additionalFilters map of filter names to filter expressions
*/
public void setAdditionalFilters(Map<String, String> additionalFilters);
/**
* Retrieves authorities including nested group memberships
* @param userData the user's LDAP context
* @param username the username
* @return collection of authorities from direct and nested groups
*/
public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username);
}No-operation authorities populator that returns empty authorities collection.
/**
* Implementation of LdapAuthoritiesPopulator that returns an empty collection of authorities
* Useful when authorities are managed outside of LDAP or when no role-based authorization is needed
*/
public class NullLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
/**
* Returns an empty collection of granted authorities
* @param userData LDAP context operations for the user (ignored)
* @param username the username (ignored)
* @return empty collection of authorities
*/
public Collection<GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username);
}/**
* Example custom authorities populator that extracts roles from user attributes
*/
public class UserAttributeAuthoritiesPopulator implements LdapAuthoritiesPopulator {
private String roleAttributeName = "employeeType";
private String rolePrefix = "ROLE_";
/**
* Sets the user attribute name containing role information
* @param roleAttributeName the attribute name
*/
public void setRoleAttributeName(String roleAttributeName);
/**
* Sets the prefix for role names
* @param rolePrefix the role prefix
*/
public void setRolePrefix(String rolePrefix);
@Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities(
DirContextOperations userData, String username) {
Set<GrantedAuthority> authorities = new HashSet<>();
// Extract roles from user attributes
String[] roles = userData.getStringAttributes(roleAttributeName);
if (roles != null) {
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(rolePrefix + role.toUpperCase()));
}
}
return authorities;
}
}/**
* Composite authorities populator that combines multiple population strategies
*/
public class CompositeAuthoritiesPopulator implements LdapAuthoritiesPopulator {
private List<LdapAuthoritiesPopulator> populators;
/**
* Creates a composite populator with the specified delegate populators
* @param populators list of authorities populators to delegate to
*/
public CompositeAuthoritiesPopulator(List<LdapAuthoritiesPopulator> populators);
@Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities(
DirContextOperations userData, String username) {
Set<GrantedAuthority> allAuthorities = new HashSet<>();
// Collect authorities from all populators
for (LdapAuthoritiesPopulator populator : populators) {
Collection<? extends GrantedAuthority> authorities =
populator.getGrantedAuthorities(userData, username);
allAuthorities.addAll(authorities);
}
return allAuthorities;
}
}@Configuration
public class LdapAuthoritiesConfig {
@Bean
public DefaultLdapAuthoritiesPopulator authoritiesPopulator() {
DefaultLdapAuthoritiesPopulator populator =
new DefaultLdapAuthoritiesPopulator(contextSource(), "ou=groups");
// Configure group search
populator.setGroupSearchFilter("member={0}");
populator.setGroupRoleAttribute("cn");
// Role configuration
populator.setRolePrefix("ROLE_");
populator.setConvertToUpperCase(true);
populator.setDefaultRole("USER");
// Search configuration
populator.setSearchSubtree(true);
populator.setIgnorePartialResultException(true);
return populator;
}
}@Bean
public DefaultLdapAuthoritiesPopulator activeDirectoryAuthorities() {
DefaultLdapAuthoritiesPopulator populator =
new DefaultLdapAuthoritiesPopulator(contextSource(), "");
// Active Directory uses memberOf attribute
populator.setGroupSearchFilter("(&(objectClass=group)(member={0}))");
populator.setGroupRoleAttribute("cn");
populator.setSearchSubtree(true);
// Active Directory specific settings
populator.setRolePrefix("ROLE_");
populator.setConvertToUpperCase(true);
return populator;
}@Component
public class HierarchicalGroupsPopulator implements LdapAuthoritiesPopulator {
private final SpringSecurityLdapTemplate ldapTemplate;
private final String groupSearchBase;
public HierarchicalGroupsPopulator(SpringSecurityLdapTemplate ldapTemplate) {
this.ldapTemplate = ldapTemplate;
this.groupSearchBase = "ou=groups";
}
@Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities(
DirContextOperations userData, String username) {
Set<GrantedAuthority> authorities = new HashSet<>();
Set<String> processedGroups = new HashSet<>();
// Find direct group memberships
Set<String> directGroups = findDirectGroups(userData.getDn().toString());
// Recursively find parent groups
for (String groupDn : directGroups) {
collectGroupHierarchy(groupDn, authorities, processedGroups);
}
return authorities;
}
private Set<String> findDirectGroups(String userDn) {
return ldapTemplate.searchForSingleAttributeValues(
groupSearchBase,
"member={0}",
new Object[]{userDn},
"distinguishedName"
);
}
private void collectGroupHierarchy(String groupDn, Set<GrantedAuthority> authorities,
Set<String> processedGroups) {
if (processedGroups.contains(groupDn)) {
return; // Avoid circular references
}
processedGroups.add(groupDn);
try {
// Get group information
DirContextOperations group = ldapTemplate.searchForContext("",
"distinguishedName={0}", new Object[]{groupDn});
String roleName = group.getStringAttribute("cn");
authorities.add(new SimpleGrantedAuthority("ROLE_" + roleName.toUpperCase()));
// Find parent groups
String[] memberOf = group.getStringAttributes("memberOf");
if (memberOf != null) {
for (String parentGroupDn : memberOf) {
collectGroupHierarchy(parentGroupDn, authorities, processedGroups);
}
}
} catch (Exception e) {
// Log and continue with other groups
logger.warn("Failed to process group: " + groupDn, e);
}
}
}@Component
public class MappedRoleAuthoritiesPopulator implements LdapAuthoritiesPopulator {
private final DefaultLdapAuthoritiesPopulator delegate;
private final Map<String, String> roleMapping;
public MappedRoleAuthoritiesPopulator(ContextSource contextSource) {
this.delegate = new DefaultLdapAuthoritiesPopulator(contextSource, "ou=groups");
this.roleMapping = initializeRoleMapping();
}
private Map<String, String> initializeRoleMapping() {
Map<String, String> mapping = new HashMap<>();
mapping.put("LDAP_ADMINS", "ADMIN");
mapping.put("LDAP_USERS", "USER");
mapping.put("LDAP_MANAGERS", "MANAGER");
mapping.put("LDAP_DEVELOPERS", "DEVELOPER");
return mapping;
}
@Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities(
DirContextOperations userData, String username) {
// Get authorities from delegate
Collection<? extends GrantedAuthority> delegateAuthorities =
delegate.getGrantedAuthorities(userData, username);
// Map to application-specific roles
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
for (GrantedAuthority authority : delegateAuthorities) {
String role = authority.getAuthority().replace("ROLE_", "");
String mappedRole = roleMapping.getOrDefault(role, role);
mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + mappedRole));
}
return mappedAuthorities;
}
}@Configuration
public class CompleteAuthenticationConfig {
@Bean
public LdapAuthenticationProvider ldapAuthenticationProvider() {
// Configure authenticator
BindAuthenticator authenticator = new BindAuthenticator(contextSource());
authenticator.setUserSearch(userSearch());
// Configure authorities populator
DefaultLdapAuthoritiesPopulator authoritiesPopulator =
new DefaultLdapAuthoritiesPopulator(contextSource(), "ou=groups");
authoritiesPopulator.setGroupSearchFilter("member={0}");
authoritiesPopulator.setGroupRoleAttribute("cn");
authoritiesPopulator.setRolePrefix("ROLE_");
authoritiesPopulator.setDefaultRole("USER");
// Create authentication provider
LdapAuthenticationProvider provider =
new LdapAuthenticationProvider(authenticator, authoritiesPopulator);
// Custom user details mapping
provider.setUserDetailsContextMapper(customUserDetailsMapper());
return provider;
}
@Bean
public UserDetailsContextMapper customUserDetailsMapper() {
LdapUserDetailsMapper mapper = new LdapUserDetailsMapper();
mapper.setRolePrefix("ROLE_");
return mapper;
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-springframework-security--spring-security-ldap