基本用法

使用 AttributesMapper 進行搜尋和查詢

以下示例使用 AttributesMapper 構建所有人員物件的公共名稱列表。

示例 1. 返回單個屬性的 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 物件,如下所示

示例 2. 返回 Person 物件的 AttributesMapper
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 物件的查詢

示例 3. 返回 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 開始執行搜尋,將返回的屬性限制為 cnsn,並使用過濾器 (&(objectclass=person)(sn=?)),其中我們希望 ?lastName 引數的值替換。以下示例展示瞭如何使用 LdapQueryBuilder 來實現

示例 4. 動態構建搜尋過濾器
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

示例 5. 使用 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 具有以下屬性

屬性名稱 屬性值

country

Sweden

company

Some Company

fullname

Some Person

前面的程式碼將產生以下識別名

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

以下示例使用 LdapUtils 從識別名中提取值

示例 6. 使用 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 新增資料

示例 7. 使用 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;
   }
}

手動構建屬性雖然繁瑣冗長,但對於許多目的來說已經足夠。但是,您可以進一步簡化繫結操作,如使用 DirContextAdapter 簡化屬性訪問和操作中所述。

刪除資料

在 Java LDAP 中刪除資料稱為解綁 (unbinding)。JNDI 解綁執行 LDAP 刪除 (Delete) 操作,從 LDAP 樹中移除與指定識別名關聯的條目。以下示例使用 LdapClient 刪除資料

示例 8. 刪除資料
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

示例 9. 使用 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。此操作接受一個顯式屬性修改陣列,並將其應用於特定條目,如下所示

示例 10. 使用 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 陣列是大量工作。然而,正如我們在使用 DirContextAdapter 簡化屬性訪問和操作中描述的,Spring LDAP 提供了更多幫助來簡化這些操作。