1. 概述
LDAP 目录服务器是针对读取优化的层次化数据存储。通常用于存储用户认证和授权所需的用户信息。
本文将探讨 Spring LDAP 的核心 API,包括:
- 用户认证
- 用户搜索
- 用户创建与修改
这些 API 同样适用于管理 LDAP 中的其他类型条目。
2. Maven 依赖
首先添加核心依赖:
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
<version>3.1.2</version>
</dependency>
最新版本可在 spring-ldap-core 查询。
3. 数据准备
为演示功能,先创建基础 LDAP 条目:
ou=users,dc=example,dc=com (objectClass=organizationalUnit)
后续所有操作(创建/修改/认证/搜索)都将在此节点下进行。
4. Spring LDAP API
4.1. ContextSource & LdapTemplate 配置
ContextSource
用于创建 LdapTemplate
,认证时需要用到:
@Bean
public LdapContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl(env.getRequiredProperty("ldap.urls"));
contextSource.setBase(
env.getRequiredProperty("ldap.partitionSuffix"));
contextSource.setUserDn(
env.getRequiredProperty("ldap.principal"));
contextSource.setPassword(
env.getRequiredProperty("ldap.password"));
return contextSource;
}
LdapTemplate
用于条目的增删改查:
@Bean
public LdapTemplate ldapTemplate() {
return new LdapTemplate(contextSource());
}
4.2. Spring Boot 集成
在 Spring Boot 项目中,使用 Spring Boot Starter Data Ldap 可自动配置 LdapContextSource
和 LdapTemplate
。
添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>
在 application.properties
配置连接参数:
spring.ldap.urls=ldap://localhost:18889
spring.ldap.base=dc=example,dc=com
spring.ldap.username=uid=admin,ou=system
spring.ldap.password=secret
直接注入自动配置的 LdapTemplate
:
@Autowired
private LdapTemplate ldapTemplate;
4.3. 用户认证
实现用户认证的核心逻辑:
public void authenticate(String username, String password) {
contextSource
.getContext(
"cn=" +
username +
",ou=users," +
env.getRequiredProperty("ldap.partitionSuffix"), password);
}
⚠️ 直接使用 getContext()
是最简单粗暴的认证方式,成功则无异常,失败抛出 AuthenticationException
。
4.4. 用户创建
创建新用户并存储密码的 SHA 哈希值:
public void create(String username, String password) {
Name dn = LdapNameBuilder
.newInstance()
.add("ou", "users")
.add("cn", username)
.build();
DirContextAdapter context = new DirContextAdapter(dn);
context.setAttributeValues(
"objectclass",
new String[]
{ "top",
"person",
"organizationalPerson",
"inetOrgPerson" });
context.setAttributeValue("cn", username);
context.setAttributeValue("sn", username);
context.setAttributeValue
("userPassword", digestSHA(password));
ldapTemplate.bind(context);
}
✅ 关键点:
digestSHA()
是自定义方法,生成密码的 SHA 哈希 Base64 字符串- 使用
bind()
方法创建条目 - 必须设置完整的
objectclass
属性链
4.5. 用户修改
修改现有用户信息:
public void modify(String username, String password) {
Name dn = LdapNameBuilder.newInstance()
.add("ou", "users")
.add("cn", username)
.build();
DirContextOperations context
= ldapTemplate.lookupContext(dn);
context.setAttributeValues
("objectclass",
new String[]
{ "top",
"person",
"organizationalPerson",
"inetOrgPerson" });
context.setAttributeValue("cn", username);
context.setAttributeValue("sn", username);
context.setAttributeValue("userPassword",
digestSHA(password));
ldapTemplate.modifyAttributes(context);
}
❗ 修改流程:
- 通过
lookupContext()
获取现有条目 - 更新属性值
- 调用
modifyAttributes()
提交修改
4.6. 用户搜索
使用过滤器搜索用户:
public List<String> search(String username) {
return ldapTemplate
.search(
"ou=users",
"cn=" + username,
(AttributesMapper<String>) attrs -> (String) attrs.get("cn").get());
}
🔍 搜索机制:
AttributesMapper
从结果条目中提取指定属性- Spring 自动对所有匹配条目执行映射,生成属性值列表
- 支持标准 LDAP 过滤器语法(如
(cn=*)
)
5. 测试
使用 spring-ldap-test
提供的嵌入式 ApacheDS 服务器进行测试:
@Bean
public TestContextSourceFactoryBean testContextSource() {
TestContextSourceFactoryBean contextSource
= new TestContextSourceFactoryBean();
contextSource.setDefaultPartitionName(
env.getRequiredProperty("ldap.partition"));
contextSource.setDefaultPartitionSuffix(
env.getRequiredProperty("ldap.partitionSuffix"));
contextSource.setPrincipal(
env.getRequiredProperty("ldap.principal"));
contextSource.setPassword(
env.getRequiredProperty("ldap.password"));
contextSource.setLdifFile(
resourceLoader.getResource(
env.getRequiredProperty("ldap.ldiffile")));
contextSource.setPort(
Integer.valueOf(
env.getRequiredProperty("ldap.port")));
return contextSource;
}
JUnit 测试示例:
@Test
public void
givenLdapClient_whenCorrectSearchFilter_thenEntriesReturned() {
List<String> users = ldapClient
.search(SEARCH_STRING);
assertThat(users, Matchers.containsInAnyOrder(USER2, USER3));
}
6. 总结
本文介绍了 Spring LDAP 的核心 API,实现了:
- 用户认证
- 用户搜索
- 用户创建与修改
完整源码见 GitHub 项目。测试位于 Maven 的 "live" profile 下,可通过 -P live
参数运行。
💡 实战建议:
- 生产环境务必使用 LDAPS 加密连接
- 密码哈希算法建议升级到更安全的 PBKDF2/BCrypt
- 复杂查询可考虑使用 Spring Data LDAP 的注解方式