基本用法
本节介绍 Spring LDAP 的基本用法。它包含以下内容
使用 AttributesMapper
进行搜索和查找
以下示例使用 AttributesMapper
构建所有人员对象的公共名称列表。
AttributesMapper
import static org.springframework.ldap.query.LdapQueryBuilder.query;
public class PersonRepoImpl implements PersonRepo {
private LdapClient ldapClient;
public void setLdapClient(LdapClient ldapClient) {
this.ldapClient = ldapClient;
}
public List<String> getAllPersonNames() {
return ldapClient.search()
.query(query().where("objectclass").is("person"))
.toList((Attributes attrs) -> (String) attrs.get("cn").get());
}
}
AttributesMapper
的内联实现从 Attributes
对象获取所需的属性值并返回它。在内部,LdapClient
遍历找到的所有条目,为每个条目调用给定的 AttributesMapper
,并将结果收集到列表中。然后,search
方法返回该列表。
请注意,可以轻松修改 AttributesMapper
实现以返回完整的 Person
对象,如下所示
import static org.springframework.ldap.query.LdapQueryBuilder.query;
public class PersonRepoImpl implements PersonRepo {
private LdapClient ldapClient;
...
private class PersonAttributesMapper implements AttributesMapper<Person> {
public Person mapFromAttributes(Attributes attrs) throws NamingException {
Person person = new Person();
person.setFullName((String)attrs.get("cn").get());
person.setLastName((String)attrs.get("sn").get());
person.setDescription((String)attrs.get("description").get());
return person;
}
}
public List<Person> getAllPersons() {
return ldapClient.search()
.query(query().where("objectclass").is("person"))
.toList(new PersonAttributesMapper());
}
}
LDAP 中的条目由其识别名 (DN) 唯一标识。如果您拥有条目的 DN,则可以直接检索该条目而无需查询。这在 Java LDAP 中称为“查找”(lookup)。以下示例显示了对 Person
对象的查找
public class PersonRepoImpl implements PersonRepo {
private LdapClient ldapClient;
...
public Person findPerson(String dn) {
return ldapClient.search().name(dn).toObject(new PersonAttributesMapper());
}
}
前面的示例查找指定的 DN 并将找到的属性传递给提供的 AttributesMapper
——在本例中,结果是一个 Person
对象。
构建 LDAP 查询
LDAP 搜索涉及许多参数,包括以下内容
-
基本 LDAP 路径:搜索应从 LDAP 树的何处开始。
-
搜索范围:搜索应在 LDAP 树中深入多远。
-
要返回的属性。
-
搜索过滤器:在范围内选择元素的标准。
Spring LDAP 提供了一个带有流畅 API 的 LdapQueryBuilder
,用于构建 LDAP 查询。
假设您想从基本 DN dc=261consulting,dc=com
开始执行搜索,将返回的属性限制为 cn
和 sn
,并使用过滤器 (&(objectclass=person)(sn=?))
,其中我们希望 ?
被 lastName
参数的值替换。以下示例展示了如何使用 LdapQueryBuilder
来实现
import static org.springframework.ldap.query.LdapQueryBuilder.query;
public class PersonRepoImpl implements PersonRepo {
private LdapClient ldapClient;
...
public List<String> getPersonNamesByLastName(String lastName) {
LdapQuery query = query()
.base("dc=261consulting,dc=com")
.attributes("cn", "sn")
.where("objectclass").is("person")
.and("sn").is(lastName);
return ldapClient.search().query(query)
.toObject((Attributes attrs) -> (String) attrs.get("cn").get());
}
}
除了简化复杂搜索参数的构建之外,LdapQueryBuilder 及其关联类还提供对搜索过滤器中任何不安全字符的正确转义。这可以防止“LDAP 注入”,即用户可能使用此类字符将不需要的操作注入到您的 LDAP 操作中。 |
LdapClient 包含许多重载方法用于执行 LDAP 搜索。这是为了尽可能适应不同的用例和编程风格偏好。对于绝大多数用例,建议使用接受 LdapQuery 作为输入的方法。 |
AttributesMapper 只是您在处理搜索和查找数据时可以使用的可用回调接口之一。有关替代方案,请参阅使用 DirContextAdapter 简化属性访问和操作。 |
有关 LdapQueryBuilder
的更多信息,请参阅高级 LDAP 查询。
动态构建识别名
识别名 (LdapName
) 的标准 Java 实现在解析识别名方面表现良好。然而,在实际使用中,此实现存在一些缺点
-
LdapName
实现是可变的,这对于表示身份的对象来说不太合适。 -
尽管其可变性,但使用
LdapName
动态构建或修改识别名的 API 很繁琐。提取索引或(特别是)命名组件的值也有点麻烦。 -
LdapName
上的许多操作会抛出受检异常,这在错误通常是致命且无法有效修复的情况下需要try-catch
语句。
为了简化识别名的使用,Spring LDAP 提供了 LdapNameBuilder
,以及 LdapUtils
中的一些实用方法,有助于处理 LdapName
。
示例
本节介绍前面各节主题的一些示例。第一个示例使用 LdapNameBuilder
动态构建 LdapName
LdapNameBuilder
动态构建 LdapName
import org.springframework.ldap.support.LdapNameBuilder;
import javax.naming.Name;
public class PersonRepoImpl implements PersonRepo {
public static final String BASE_DN = "dc=example,dc=com";
protected Name buildDn(Person p) {
return LdapNameBuilder.newInstance(BASE_DN)
.add("c", p.getCountry())
.add("ou", p.getCompany())
.add("cn", p.getFullname())
.build();
}
...
}
假设 Person
具有以下属性
属性名称 | 属性值 |
---|---|
|
Sweden |
|
Some Company |
|
Some Person |
前面的代码将产生以下识别名
cn=Some Person, ou=Some Company, c=Sweden, dc=example, dc=com
以下示例使用 LdapUtils
从识别名中提取值
LdapUtils
从识别名中提取值import org.springframework.ldap.support.LdapNameBuilder;
import javax.naming.Name;
public class PersonRepoImpl implements PersonRepo {
...
protected Person buildPerson(Name dn, Attributes attrs) {
Person person = new Person();
person.setCountry(LdapUtils.getStringValue(dn, "c"));
person.setCompany(LdapUtils.getStringValue(dn, "ou"));
person.setFullname(LdapUtils.getStringValue(dn, "cn"));
// Populate rest of person object using attributes.
return person;
}
}
由于 Java 1.4 及之前的版本根本没有提供任何公共的识别名实现,Spring LDAP 1.x 提供了自己的实现 DistinguishedName
。此实现自身存在一些缺点,并在 2.0 版本中已弃用。现在应使用 LdapName
以及前面描述的工具类。
绑定和解绑
本节介绍如何添加和删除数据。更新将在下一节中介绍。
添加数据
在 Java LDAP 中插入数据称为绑定 (binding)。这有点令人困惑,因为在 LDAP 术语中,“bind”的意思完全不同。JNDI 绑定执行 LDAP 添加 (Add) 操作,将具有指定识别名的新条目与一组属性关联起来。以下示例使用 LdapClient
添加数据
public class PersonRepoImpl implements PersonRepo {
private LdapClient ldapClient;
...
public void create(Person p) {
Name dn = buildDn(p);
ldapClient.bind(dn).attributes(buildAttributes(p)).execute();
}
private Attributes buildAttributes(Person p) {
Attributes attrs = new BasicAttributes();
BasicAttribute ocattr = new BasicAttribute("objectclass");
ocattr.add("top");
ocattr.add("person");
attrs.put(ocattr);
attrs.put("cn", "Some Person");
attrs.put("sn", "Person");
return attrs;
}
}
手动构建属性虽然繁琐冗长,但对于许多目的来说已经足够。但是,您可以进一步简化绑定操作,如使用 DirContextAdapter
简化属性访问和操作中所述。
删除数据
在 Java LDAP 中删除数据称为解绑 (unbinding)。JNDI 解绑执行 LDAP 删除 (Delete) 操作,从 LDAP 树中移除与指定识别名关联的条目。以下示例使用 LdapClient
删除数据
public class PersonRepoImpl implements PersonRepo {
private LdapClient ldapClient;
...
public void delete(Person p) {
Name dn = buildDn(p);
ldapClient.unbind(dn).execute();
}
}
更新
在 Java LDAP 中,可以通过两种方式修改数据:使用 rebind
或使用 modifyAttributes
。
使用 Rebind 进行更新
rebind
是一种粗略的数据修改方式。它本质上是一个 unbind
紧跟着一个 bind
。以下示例调用 LDAP 的 rebind
public class PersonRepoImpl implements PersonRepo {
private LdapClient ldapClient;
...
public void update(Person p) {
Name dn = buildDn(p);
ldapTemplate.bind(dn).attributes(buildAttributes(p)).replaceExisting(true).execute();
}
}
使用 modifyAttributes
进行更新
一种更复杂的数据修改方式是使用 modifyAttributes
。此操作接受一个显式属性修改数组,并将其应用于特定条目,如下所示
public class PersonRepoImpl implements PersonRepo {
private LdapClient ldapClient;
...
public void updateDescription(Person p) {
Name dn = buildDn(p);
Attribute attr = new BasicAttribute("description", p.getDescription())
ModificationItem item = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
ldapTemplate.modify().name(dn).attributes(item).execute();
}
}
构建 Attributes
和 ModificationItem
数组是大量工作。然而,正如我们在使用 DirContextAdapter
简化属性访问和操作中描述的,Spring LDAP 提供了更多帮助来简化这些操作。