基本用法

使用 AttributesMapper 進行搜尋與查詢

以下範例使用 AttributesMapper 來建立包含所有 person 物件通用名稱的清單。

範例 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. 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 中稱為「查詢」。以下範例顯示 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 提供 LdapQueryBuilder,具有流暢的 API 用於建立 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 搜尋的 overloaded 方法。這是為了適應盡可能多的不同使用案例和程式設計風格偏好。對於絕大多數使用案例,建議使用將 LdapQuery 作為輸入的方法。
AttributesMapper 只是您可以在處理搜尋和查詢資料時使用的可用回呼介面之一。如需替代方案,請參閱 使用 DirContextAdapter 簡化屬性存取與操作

如需 LdapQueryBuilder 的詳細資訊,請參閱 進階 LDAP 查詢

動態建立識別名稱

Distinguished Name (LdapName) 的標準 Java 實作在解析識別名稱方面表現良好。但是,在實際使用中,此實作有許多缺點

  • LdapName 實作是可變的,這與代表身分識別的物件非常不符。

  • 儘管 LdapName 具有可變性,但使用 LdapName 動態建立或修改識別名稱的 API 相當繁瑣。提取索引或(特別是)具名元件的值也有點笨拙。

  • LdapName 上的許多操作都會擲回 checked exceptions,對於錯誤通常是致命且無法以有意義的方式修復的情況,需要 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;
  }
}

由於 1.4 及更早版本的 Java 版本根本沒有提供任何公用識別名稱實作,因此 Spring LDAP 1.x 提供了自己的實作 DistinguishedName。此實作本身有一些缺點,並且已在 2.0 版中棄用。您現在應該使用 LdapName 以及先前描述的公用程式。

繫結與解除繫結

本節說明如何新增和移除資料。更新將在下一節中介紹。

新增資料

在 Java LDAP 中插入資料稱為繫結。這有點令人困惑,因為在 LDAP 術語中,「繫結」表示完全不同的意思。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 中移除資料稱為解除繫結。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 提供了更多協助來簡化這些操作。