Basic Usage

Search and Lookup Using AttributesMapper

以下示例使用 AttributesMapper 来构建所有用户对象的通用名称列表。

Example 1. AttributesMapper that returns a single attribute
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 对象,如下所示:

Example 2. AttributesMapper that returns a Person object
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 中的条目通过它们的 distinguished name (DN) 来唯一标识。如果您有条目的 DN,则可以直接检索该条目,而不必查询它。在 Java LDAP 中这称为“lookup”。以下示例展示如何查找 Person 对象:

Example 3. A lookup resulting in a Person object
public class PersonRepoImpl implements PersonRepo {
   private LdapClient ldapClient;
   ...
   public Person findPerson(String dn) {
      return ldapClient.search().name(dn).toObject(new PersonAttributesMapper());
   }
}

前面的示例查找指定的 DN,并将找到的属性传递给提供的 AttributesMapper,在这种情况下,将生成一个 Person 对象。

Building LDAP Queries

LDAP 搜索涉及一些参数,包括以下内容:

  • 基础 LDAP 路径:搜索应在 LDAP 树中的哪个位置开始。

  • 搜索范围:搜索应在 LDAP 树中深入到哪个级别。

  • Attributes to return.

  • 搜索过滤器:在选择范围内的元素时要使用的条件。

Spring LDAP 提供一个带有用于构建 LDAP 查询的流畅 API 的 LdapQueryBuilder

假设你要执行从基本 DN dc=261consulting,dc=com 开始的搜索,将返回的属性限制为 cnsn,过滤器为 (&(objectclass=person)(sn=?)),其中我们要将 ? 替换为 lastName 参数。以下示例演示了如何使用 LdapQueryBuilder 进行此操作:

Example 4. Building a search filter dynamically
*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 injection”,其中用户可能会使用此类字符将不需要的操作注入到 LDAP 操作中。

LdapClient 包含用于执行 LDAP 搜索的许多重载方法。这是为了适应尽可能多的不同用例和编程风格偏好。对于绝大多数用例,将 LdapQuery 作为输入的方法是推荐使用的方法。

AttributesMapper 只是在处理搜索和查找数据时可以使用的可用回调接口之一。有关替代方式,请参阅 Simplifying Attribute Access and Manipulation with DirContextAdapter

有关 LdapQueryBuilder 的更多信息,请参见 Advanced LDAP Queries

Dynamically Building Distinguished Names

在解析识别名时,识别名的标准 Java 实现 ( LdapName) 运行良好。然而,在实际使用中,此实现有一些缺点:

  • `LdapName`实现是可变的,这并不适合表示标识的对象。

  • 尽管具有可变性,但使用 `LdapName`动态构建还是修改专有名称的 API 非常繁琐。提取索引值或(尤其)的命名组件值也有些麻烦。

  • `LdapName`中的许多操作都会抛出已检查异常,在错误通常是致命的并且无法以有意义的方式修复时,需要 `try-catch`语句。

为了简化识别名的使用,Spring LDAP 提供 LdapNameBuilder,以及 LdapUtils 中的一些实用方法,这些方法在处理 LdapName 时有所帮助。

Examples

本节提供几个在前面各节中涵盖的主题的示例。第一个示例使用 LdapNameBuilder 动态构建 LdapName

Example 5. Dynamically building an LdapName by using LdapNameBuilder
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 具有以下属性:

Attribute Name Attribute Value

country

Sweden

company

Some Company

fullname

Some Person

然后前述代码将生成以下 distinguished name:

cn=Some Person, ou=Some Company, c=Sweden, dc=example, dc=com

以下示例使用 LdapUtils 从 distinguished name 中提取值

Example 6. Extracting values from a distinguished name by using 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;
  }
}

由于 1.4 及更早版本的 Java 根本不提供任何公共 Distinguished Name 实现,Spring LDAP 1.x 因此提供了自己的实现 DistinguishedName。该实现自有几个缺陷,并且已在 2.0 版中弃用。您现在应使用 LdapName 以及前面描述的实用程序。

Binding and Unbinding

本部分介绍如何增添和删除数据。更新的内容涵盖在 next section 中。

Adding Data

在 Java LDAP 中插入数据称为绑定。这有些令人困惑,因为在 LDAP 术语中,“bind”表示完全不同的内容。JNDI 绑定执行 LDAP Add 操作,将具有指定 distinguished name 的新条目与一组属性相关联。以下示例使用 LdapClient 添加数据:

Example 7. Adding data using Attributes
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;
   }
}

手动构建属性虽然单调且冗长,但足以满足很多目的。然而,你可以进一步简化绑定操作,如 Simplifying Attribute Access and Manipulation with DirContextAdapter 所述。

Removing Data

在 Java LDAP 中删除数据称为解除绑定。JNDI 解除绑定执行 LDAP Delete 操作,从 LDAP 树中删除与指定 distinguished name 相关联的条目。以下示例使用 LdapClient 删除数据:

Example 8. Removing data
public class PersonRepoImpl implements PersonRepo {
   private LdapClient ldapClient;
   ...
   public void delete(Person p) {
      Name dn = buildDn(p);
      *ldapClient.unbind(dn).execute();*
   }
}

Updating

在 Java LDAP 中,可以通过两种方式修改数据:使用 rebind,或者使用 modifyAttributes

Updating by Using Rebind

rebind 是一种原始的数据修改方式。它实质上是一个 unbind,后跟一个 bind。以下示例调用 LDAP 的 rebind

Example 9. Modifying using 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();*
   }
}

Updating by Using modifyAttributes

一种更复杂的数据修改方式是使用 modifyAttributes。此操作采用一个显式属性修改数组,并将其应用于一个特定的条目,如下图所示:

Example 10. Modifying using 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();*
   }
}

构建 AttributesModificationItem 数组需要很多工作。但是,正如我们在 Simplifying Attribute Access and Manipulation with DirContextAdapter 中所述,Spring LDAP 提供更多帮助以简化这些操作。