モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

Spring LDAP の基本的な使い方 (Spring Boot)

モーダルを閉じる

ステッカーを選択してください

お支払い手続きへ
モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2017/07/27
最終更新最終更新
2021/09/07
記事区分記事区分
一般公開

目次

    Spring Bootでの実用的な機能をわかりやすく解説中

    本ページではユーザー認証を LDAP サーバーからの情報をもとに行います。

    Spring LDAP が提供する LDAP クライアントを Spring Boot から利用します。LDAP サーバーは、Spring Boot のドキュメントに記載のある UnboundID を利用します。メモリ上で動作し、アプリケーション起動時にリソースファイルをもとに初期化される、開発時に便利な簡易サーバーです。

    公式ドキュメント

    LDAP について

    解説動画 Spring LDAP 2.0.0

    サンプルプロジェクト

    基本的な Gradle プロジェクトです。

    .
    |-- build.gradle
    |-- gradle
    |   `-- wrapper
    |       |-- gradle-wrapper.jar
    |       `-- gradle-wrapper.properties
    |-- gradlew
    |-- gradlew.bat
    `-- src
        `-- main
            |-- java
            |   `-- hello
            |       |-- Application.java
            |       |-- HelloController.java
            |       `-- WebSecurityConfig.java
            `-- resources
                |-- application.yml
                `-- test-server.ldif
    

    build.gradle

    buildscript {
        ext {
            springBootVersion = '1.5.3.RELEASE'
        }
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        }
    }
    
    apply plugin: 'java'
    apply plugin: 'eclipse'
    apply plugin: 'idea'
    apply plugin: 'org.springframework.boot'
    
    jar {
        baseName = 'gs-spring-boot'
        version =  '0.1.0'
    }
    
    repositories {
        mavenCentral()
    }
    
    sourceCompatibility = 1.8
    targetCompatibility = 1.8
    
    dependencies {
        compile('org.springframework.boot:spring-boot-starter-web')
        compile('org.springframework.boot:spring-boot-starter-security')
        compile('org.springframework.boot:spring-boot-starter-data-ldap')
        compile('org.springframework.security:spring-security-ldap')
        compile('com.unboundid:unboundid-ldapsdk') // UnboundId, an open source LDAP server.
    }
    

    src/main/resources/application.yml

    アプリケーション設定ファイルにアプリ内蔵の簡易 LDAP サーバ UnboundID の設定を追加します。

    spring:
      ldap:
        embedded:
          base-dn: dc=springframework,dc=org
          ldif: classpath:test-server.ldif
          port: 8389
    

    src/main/java/hello/Application.java

    package hello;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    

    src/main/java/hello/HelloController.java

    HTTP レスポンスのボディで返す文字列を直接コントローラで指定する @RestController アノテーションを利用しています。

    package hello;
    
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @RestController
    public class HelloController {
    
        @RequestMapping("/")
        public String index() {
            return "Greetings from Spring Boot!";
        }
    }
    

    src/main/java/hello/WebSecurityConfig.java

    Spring Security の利用時と同様に、@EnableWebSecurity アノテーションが設定されて WebSecurityConfigurerAdapter を継承したクラスで Spring Security の既定の挙動をカスタマイズできます。

    • .authorizeRequests() すべてのエンドポイントで認証が必要になるように設定しています。
    • .formLogin() ログイン認証用のエンドポイントが自動生成されるように設定しています。既定値のパス /login に対する HTML ログインフォームページが生成されます。
    • .ldapAuthentication() ログイン認証で LDAP サーバからの情報を利用するように設定しています。入力されたユーザー名は uid={0} で LDAP 検索時に利用されます。入力されたパスワードは LdapShaPasswordEncoder() でハッシュ値を計算して、属性名 userPassword の値と比較されます。

    WebSecurityConfig.java

    package hello;
    
    import java.util.Arrays;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.encoding.LdapShaPasswordEncoder;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
    
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .authorizeRequests()
                    .anyRequest().fullyAuthenticated()
                    .and()
                .formLogin();
        }
    
        @Override
        public void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth
                .ldapAuthentication()
                    .userDnPatterns("uid={0},ou=people")
                    .groupSearchBase("ou=groups")
                    .contextSource(contextSource())
                    .passwordCompare()
                        .passwordEncoder(new LdapShaPasswordEncoder())
                        .passwordAttribute("userPassword");
        }
    
        @Bean
        public DefaultSpringSecurityContextSource contextSource() {
            return new DefaultSpringSecurityContextSource(Arrays.asList("ldap://localhost:8389/"), "dc=springframework,dc=org");
        }
    }
    

    src/main/resources/test-server.ldif

    Spring LDAP チュートリアルに記載の LDIF (LDAP Data Interchange Format) 情報をそのまま利用します。ログインユーザー名は ben、パスワードは benspassword です。

    dn: dc=springframework,dc=org
    objectclass: top
    objectclass: domain
    objectclass: extensibleObject
    dc: springframework
    
    ...
    
    dn: uid=ben,ou=people,dc=springframework,dc=org
    objectclass: top
    objectclass: person
    objectclass: organizationalPerson
    objectclass: inetOrgPerson
    cn: Ben Alex
    sn: Alex
    uid: ben
    userPassword: {SHA}nFCebWjxfaLbHHG1Qk5UU4trbvQ=
    
    ...
    
    • dn Distinguished Name → ディレクトリ内で一意に識別するための値
    • dc Domain Component → ドメインを構成するための要素 (例えば www.google.comdc=www,dc=google,dc=com となります)
    • ou Organizational Unit → あるドメイン内の組織
    • cn Common Name → ある組織内の人やもの

    LDAP クライアントの使い方

    上記サンプルプロジェクトでは、LDAP クライアントを Spring Security の認証で、あまり意識することなく内部的に利用しました。以下では、Spring LDAP が提供する LDAP クライアントを直接利用するサンプルコードをまとめます。簡単のため、HelloController.java 内で LDAP クライアントを利用しますが、実際のアプリケーションでは @Repository Bean や @Service Bean で利用する設計が好ましいとされます。

    CN 一覧を検索

    src/main/resources/application.yml

    LDAP サーバーへの接続に必要な情報を設定します。LdapContextSource に値が設定されて @Autowired した LdapTemplate で利用されます。

    spring:
      ldap:
        embedded:
          base-dn: dc=springframework,dc=org
          ldif: classpath:test-server.ldif
          port: 8389
        context-source:
          url: ldap://localhost:8389
          base: dc=springframework,dc=org
          username:
          password:
    

    src/main/java/hello/HelloController.java

    今回のサンプルでは扱いませんが、context-source の値を動的にリクエスト毎に設定したい場合は、LdapContextSource を new で新規に生成して、それを引数に LdapTemplate を new で新規に生成して利用します。LdapTemplate@Autowired は行いません。

    package hello;
    
    import static org.springframework.ldap.query.LdapQueryBuilder.query;
    
    import java.util.List;
    
    import javax.naming.NamingException;
    import javax.naming.directory.Attributes;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.ldap.core.AttributesMapper;
    import org.springframework.ldap.core.LdapTemplate;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class HelloController {
    
        @Autowired
        private LdapTemplate ldapTemplate;
    
        public List<String> getAllPersonNames() {
            return ldapTemplate.search(
                query().where("objectclass").is("person"),
                new AttributesMapper<String>() {
                    public String mapFromAttributes(Attributes attrs) throws NamingException {
                        return (String) attrs.get("cn").get();
                    }
                });
        }
    
        @RequestMapping("/")
        public String index() {
            return getAllPersonNames().toString();
        }
    }
    

    レスポンス例

    [quote\"guy, Joe Smeth, Mouse, Jerry, slash/guy, Ben Alex, Bob Hamilton, Space Cadet]
    

    1000 件を越えるデータを取得する

    LDAP サーバで一度に取得可能な最大件数 1000 が設定されていることがあります。そのような場合は Paged Search Results に記載の内容を参考に、ldapsearch コマンドの -E pr=1000/noprompt に相当する処理を Java で行います。

    DN で検索

    src/main/resources/application.yml

    「CN 一覧を検索」の場合と同様の設定を行います。

    src/main/java/hello/HelloController.java

    ディレクトリ内のエントリー識別子 DN を引数にして lookup で検索します。

    package hello;
    
    import javax.naming.NamingException;
    import javax.naming.directory.Attributes;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.ldap.core.AttributesMapper;
    import org.springframework.ldap.core.LdapTemplate;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class HelloController {
    
        @Autowired
        private LdapTemplate ldapTemplate;
    
        public String findPersonName(String dn) {
            return ldapTemplate.lookup(dn, new AttributesMapper<String>() {
                public String mapFromAttributes(Attributes attrs) throws NamingException {
                    return (String) attrs.get("cn").get();
                }
            });
        }
    
        @RequestMapping("/")
        public String index() {
            return findPersonName("uid=ben,ou=people");
        }
    }
    

    レスポンス例

    Ben Alex
    

    ContextMapper の利用

    AttributesMapper ではなく ContextMapper を利用することもできます。以下は lookup の例ですが search でも同様です。

    package hello;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.ldap.core.ContextMapper;
    import org.springframework.ldap.core.DirContextAdapter;
    import org.springframework.ldap.core.LdapTemplate;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class HelloController {
    
        @Autowired
        private LdapTemplate ldapTemplate;
    
        public String findPersonName(String dn) {
            return ldapTemplate.lookup(dn, new ContextMapper<String>() {
                public String mapFromContext(Object ctx) {
                    DirContextAdapter context = (DirContextAdapter)ctx;
                    return context.getStringAttribute("cn");
                }
            });
        }
    
        @RequestMapping("/")
        public String index() {
            return findPersonName("uid=ben,ou=people");
        }
    }
    
    Likeボタン(off)0
    詳細設定を開く/閉じる
    アカウント プロフィール画像

    Spring Bootでの実用的な機能をわかりやすく解説中

    記事の執筆者にステッカーを贈る

    有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

    >>さらに詳しくステッカーを贈る
    ステッカーを贈る コンセプト画像

    Feedbacks

    Feedbacks コンセプト画像

      ログインするとコメントを投稿できます。

      ログインする

      関連記事

      • Spring Security フォームログインのサンプルコード
        Spring フレームワークによる Web アプリケーション開発で、ログイン処理を実装する際は Spring Security が便利です。ここでは特に Spring Boot で Web アプリケーションを開発する場合を対象とし、フォームによる ID/Password ログインを行うためのサンプルコードをまとめます。 公式ドキュメント [Spring Security チュートリアル](http...
        えびちゃんえびちゃん11/4/2019に更新
        いいねアイコン画像0
      • Java配列の宣言方法 (C/C++との違い)
        Javaの配列 Javaの配列宣言方法はC/C++と似ているようで若干異なる。 初期化しない場合 C/C++の int array[10]; はJavaでは int array[] = new int[10]; となる。同様にC/C++の int array[3][3]; はJavaでは int array[][] = new int[3][3]; となる。 初期化
        てんとうむしてんとうむし4/13/2018に更新
        いいねアイコン画像0
      • PlantUML による UML 図の描き方
        サムネイル画像-c788fffde5
        PlantUML はテキスト形式で表現されたシーケンス図やクラス図といった UML (Unified Modeling Language) 図の情報から画像を生成するためのツールです。簡単な使い方をまとめます。 インストール方法の選択 Atom や Eclipse のプラグインをインストールしてエディタから利用する方法、JAR をダウンロードして Java コマンドで実行する方法、Redmine ...
        kentakenta12/21/2019に更新
        いいねアイコン画像0
      • Akka HTTP サンプルコード (Scala)
        サムネイル画像-a98142497c
        Akka アクターを用いて実装された汎用 HTTP フレームワークです。Spray の後継です。コアモジュールである akka-http-core は 2016/2/17 に experimental が外れました。akka-http などのいくつかのサブモジュールは 2016/3/1 現在 experimental のままですが、基本的な
        雄太雄太9/7/2021に更新
        いいねアイコン画像0
      • Kestrel の使用例
        Kestrel は Message Queue (MQ) の実装のひとつです。一般に MQ はアプリケーション間やプロセス間、スレッド間で非同期に通信するために用いられます。メッセージの送信側は MQ に書き込めば受信側の応答を待たずに次の処理に非同期に進むことができます。Kestrel はわずか 2500 行程の Scala で実装されており JVM で動作します。MQ 自体はメモリ上に存在する...
        したくんしたくん9/12/2017に更新
        いいねアイコン画像0