昨日の 記事 に続きまして, 今回はごくごく簡単な SEARCH をおこない, Apache Directory LDAP API と UnboundID LDAP SDK for Java とを比較してみました。
Apache Directory LDAP API の場合
手元の環境にある LDAP サーバーで Von Erich さんを探してみたいと思います。 たとえば, 次のようになりましょうか。
LdapConnection conn; // ... String base = "dc=localdomain"; String filter = "(sn=Von Erich)"; SearchScope scope = SearchScope.SUBTREE; Cursor<Entry> cursor = conn.search(base, filter, scope);
Apache Directory LDAP API では, LDAP サーバーからのレスポンスを取り扱うには, カーソルを利用する (というか, カーソルのようなものがあるのだと仮定して, それを利用する) というコンセプトになるのですね。 LDAP 関連ではカーソルを使うという話題はあまりネット上で見かけた記憶もなかったので, 目新しい発想だなあと感じました。 その, カーソル的なものを使って, レスポンスに含まれる個々のエントリーの DN を標準出力に書き出すなら, たとえば次のようになります。
try { while (cursor.next()) { Entry entry = cursor.get(); System.out.println(entry.getDn().toString()); } } catch (CursorException | LdapException e) { // ... } finally { cursor.close(); }
Cursor
インターフェースの next()
や get()
が CursorException
や LdapException
をスローする可能性があるほか, 使い終わったカーソルは閉じなければなりませんので, ここでは try-catch-finally 構文のお世話になっています。
実行すると, 次のような結果を得ることができました。
cn=Fritz Von Erich,ou=Iron Claw,dc=localdomain cn=Kevin Von Erich,ou=Iron Claw,dc=localdomain cn=David Von Erich,ou=Iron Claw,dc=localdomain cn=Kerry Von Erich,ou=Iron Claw,dc=localdomain cn=Mike Von Erich,ou=Iron Claw,dc=localdomain cn=Chris Von Erich,ou=Iron Claw,dc=localdomain
ところで, LDAP インジェクション対策という観点からは, フィルターの文字列を自分で組み立てて用意するのはできれば避けたいところです。 そこで, API 側で用意されているプレースホルダー的なものに救いを求めることを考えてみます。 具体的には SearchRequest
を使って, たとえば次のように書き改めることにします。 (なお, Cursor
のメソッドがスローする可能性のある例外以外の例外については, 処理を省略しました。)
LdapConnection conn; // ... Dn base = new Dn("dc=localdomain"); ExprNode filter = new EqualityNode("sn", new StringValue("Von Erich")); SearchScope scope = SearchScope.SUBTREE; SearchRequest request = new SearchRequestImpl() .setBase(base) .setFilter(filter) .setScope(scope); Cursor<Response> cursor = conn.search(request); try { while (cursor.next()) { Response response = cursor.get(); if (response instanceof SearchResultEntry) { Entry entry = ((SearchResultEntry)response).getEntry(); System.out.println(entry.getDn().toString()); } } } catch (CursorException | LdapException e) { // ... } finally { cursor.close(); }
ExprNode
のインスタンスがフィルターを表現することになりますが, search
メソッドにはそれを直接引数として受け取るものがありません。 そのため, SearchRequest
のインスタンス経由で渡すことを考えてみました。 それがよかったのかいけなかったのかわかりませんが, 先ほどとは若干様相が異なってしまいました。 たとえば, search
メソッドが, Cursor<Entry>
ではなく Cursor<Response>
を返すように変化していますが, なぜそうなるのか, わたしにはまだよくわかっていません。 ただ, instanceof
を使わないといけない箇所があったり, SearchResultEntry
は Entry
のサブインターフェースなのかと思いきや実はまったく親子関係がなかったり, なんというか, あまり見通しがよくなくなってしまったなあ..という感触を覚えます。
UnboundID LDAP SDK for Java の場合
同じことを UnboundID LDAP SDK for Java でやろうとしたら, 次のようになりました。 (なお, search
がスローする可能性のある例外については, 処理を省略しました。)
LDAPConnection conn; // ... String base = "dc=localdomain"; SearchScope scope = SearchScope.SUB; Filter filter = Filter.createEqualityFilter("sn", "Von Erich"); SearchResult result = conn.search(base, scope, filter); Listentries = result.getSearchEntries(); for (SearchResultEntry entry : entries) { System.out.println(entry.getDN()); }
コンパクトに書くことができました。
細かいところで少し感心した点のは, スコープについてです。 というのも, BASE
(0), ONE
(1), SUB
(2) のほか, SUBORDINATE_SUBTREE
(3) を選択することもできるのです。 この最後の選択肢は, まだ LDAPv3 の標準仕様に正式に追加されていません。 だからでしょうか, JNDI も Apache Directory LDAP API も, 現状これをサポートしていません。
また, 上記コードでは, JNDI 的な Enumeration
でもなく, Apache 的な Cursor
でもなく, 検索結果を List
として取得することができます。 なんというか, うれしい気がします。 ちなみに, 実体は編集不可のリスト (unmodifiable list) ですので add
や remove
などでいたずらをしようとすれば, UnsupportedOperationException
が飛んできます。
感想
昨日は何とも判断がつきかねておりましたが, 今回は UnboundID LDAP SDK for Java のほうがだいぶ気に入りました。
No comments:
Post a Comment