1. 概要

LDAPディレクトリサーバーは、読み取りに最適化された階層データストアです。 通常、これらは、ユーザーの認証と承認に必要なユーザー関連情報を保存するために使用されます。

この記事では、Spring LDAP APIを使用してユーザーを認証および検索し、ディレクトリサーバーでユーザーを作成および変更します。 同じAPIのセットを使用して、LDAP内の他のタイプのエントリを管理できます。

2. Mavenの依存関係

必要なMaven依存関係を追加することから始めましょう:

<dependency>
    <groupId>org.springframework.ldap</groupId>
    <artifactId>spring-ldap-core</artifactId>
    <version>2.3.1.RELEASE</version>
</dependency>

この依存関係の最新バージョンは、spring-ldap-coreにあります。

3. データの準備

この記事の目的のために、最初に次のLDAPエントリを作成しましょう。

ou=users,dc=example,dc=com (objectClass=organizationalUnit)

このノードの下で、新しいユーザーを作成し、既存のユーザーを変更し、既存のユーザーを認証し、情報を検索します。

4. Spring LDAP API

4.1. ContextSource LdapTemplate Bean定義

ContextSource は、LdapTemplateの作成に使用されます。 次のセクションでは、ユーザー認証中にContextSourceが使用されることを確認します。

@Bean
public LdapContextSource contextSource() {
    LdapContextSource contextSource = new LdapContextSource();
    
    contextSource.setUrl(env.getRequiredProperty("ldap.url"));
    contextSource.setBase(
      env.getRequiredProperty("ldap.partitionSuffix"));
    contextSource.setUserDn(
      env.getRequiredProperty("ldap.principal"));
    contextSource.setPassword(
      env.getRequiredProperty("ldap.password"));
    
    return contextSource;
}

LdapTemplate は、LDAPエントリの作成と変更に使用されます。

@Bean
public LdapTemplate ldapTemplate() {
    return new LdapTemplate(contextSource());
}

4.2. Spring Bootを使用する

Spring Bootプロジェクトで作業しているときは、 Spring Boot Starter Data Ldap 依存関係を使用して、LdapContextSourceLdapTemplateを自動的にインストルメントします。 

自動構成を有効にするには、spring-boot-starter-data-ldapスターターまたはspring-ldap-coreがpom.xmlの依存関係として定義されていることを確認する必要があります。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency>

LDAPに接続するには、application.propertiesで接続設定を提供する必要があります。

spring.ldap.url=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);
}

4.4. ユーザーの作成

次に、新しいユーザーを作成し、パスワードのSHAハッシュをLDAPに保存しましょう。

認証時に、LDAPサーバーは指定されたパスワードの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エンコード文字列を返すカスタムメソッドです。

最後に、 LdapTemplatebind()メソッドを使用して、LDAPサーバーにエントリを作成します。

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()メソッドは、指定されたユーザーを見つけるために使用されます。

4.6. ユーザー検索

検索フィルターを使用して既存のユーザーを検索できます。

public List<String> search(String username) {
    return ldapTemplate
      .search(
        "ou=users", 
        "cn=" + username, 
        (AttributesMapper<String>) attrs -> (String) attrs.get("cn").get());
}

attributeMapper は、見つかったエントリから目的の属性値を取得するために使用されます。 内部的には、Spring LdapTemplateは見つかったすべてのエントリに対してAttributesMapperを呼び出し、属性値のリストを作成します。

5. テスト

spring-ldap-test は、ApacheDS1.5.5に基づく組み込みLDAPサーバーを提供します。 テスト用に組み込みLDAPサーバーをセットアップするには、次のSpringBeanを構成する必要があります。

@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を紹介し、LDAPサーバーでのユーザー認証、ユーザー検索、ユーザー作成、および変更のための簡単な方法を開発しました。

いつものように、完全なソースコードはこのGithubプロジェクトで入手できます。 テストはMavenプロファイル「live」で作成されるため、オプション「-Plive」を使用して実行できます。