No. 5 Javaからの接続。
参考 http://codezine.jp/a/article/aid/200.aspx?p=2
上記サイトが詳しい。このサイトを見て、もう少し生々しい形のソースを書いた。
クラスの詳細な解説等は、上記サイトを見たほうがいい。
Javaのバージョンは5.0を前提としている。
バインドとアンバインド
staticメソッドで、接続のサンプル。
package net.kuwalab; import java.util.Hashtable; import javax.naming.Context; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; public class LdapTest { public static void main(String[] args) { // LDAP接続 DirContext ctx = connect("ldap://192.168.0.201", null, "dc=kuwalab,dc=net", "uid=namakuwa,ou=user", "test"); if (ctx == null) { System.out.println("接続失敗"); return; } System.out.println("接続成功"); close(ctx); } private static DirContext connect(String url, String port, String dn, String id, String password) { DirContext ctx = null; try { Hashtable<String, String> env = new Hashtable<String, String>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); StringBuilder sb = new StringBuilder(); sb.append(url); if (port != null) { sb.append(":").append(port); } env.put(Context.PROVIDER_URL, sb.toString()); env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_PRINCIPAL, id + "," + dn); env.put(Context.SECURITY_CREDENTIALS, password); ctx = new InitialDirContext(env); } catch (Exception e) { e.printStackTrace(); if (ctx != null) { try { ctx.close(); } catch (Exception ex) { // 無視 } ctx = null; } } return ctx; } private static void close(DirContext ctx) { if (ctx != null) { try { ctx.close(); } catch (Exception ex) { // 無視 } } } }
特に難しいことはない。urlは、ldapsのテストもしたいのでプロトコルからメソッドに渡している。
URLは一生懸命StringBuilderで組み立てているが、PRINCIPALは「+」で連結している。
これをベースに、検索をしてみる。
検索
最初の例は、staticメソッドだったが、やっぱりあまりにもなので作り変える。
package net.kuwalab; import java.util.Enumeration; import java.util.Hashtable; import javax.naming.Context; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; public class LdapTest { private DirContext ctx; public static void main(String[] args) { // LDAP接続 LdapTest lt = new LdapTest(); boolean result = lt.connect("ldap://192.168.0.201", null, "dc=kuwalab,dc=net", "uid=namakuwa,ou=user", "test"); if (!result) { System.out.println("接続失敗"); return; } System.out.println("接続成功"); lt.search("uid=namakuwa,ou=user,dc=kuwalab,dc=net", "uid=namakuwa"); lt.close(); result = lt.connect("ldap://192.168.0.201", null, "dc=kuwalab,dc=net", "cn=Manager", "test"); if (!result) { System.out.println("接続失敗"); return; } System.out.println("接続成功"); lt.search("ou=user,dc=kuwalab,dc=net", "uid=*"); lt.close(); } public boolean connect(String url, String port, String dn, String id, String password) { try { Hashtable<String, String> env = new Hashtable<String, String>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); StringBuilder sb = new StringBuilder(); sb.append(url); if (port != null) { sb.append(":").append(port); } env.put(Context.PROVIDER_URL, sb.toString()); env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_PRINCIPAL, id + "," + dn); env.put(Context.SECURITY_CREDENTIALS, password); ctx = new InitialDirContext(env); } catch (Exception e) { e.printStackTrace(); if (ctx != null) { try { ctx.close(); } catch (Exception ex) { // 無視 } ctx = null; } return false; } return true; } public void close() { if (ctx != null) { try { ctx.close(); } catch (Exception ex) { // 無視 } } } public void search(String baseDn, String filter) { SearchControls searchControls = new SearchControls(); // 以下のURL参照。 // http://java.sun.com/j2se/1.5.0/ja/docs/ja/api/javax/naming/directory/SearchControls.html searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE); try { // 検索結果のエントリを取得 NamingEnumeration<SearchResult> result = ctx.search(baseDn, filter, searchControls); // エントリを1件ずつ処理 while (result.hasMore()) { SearchResult sr = result.next(); // エントリ NamingEnumeration attributes = sr.getAttributes().getAll(); // エントリに含まれる属性を1件ずつ取得して処理 while (attributes.hasMore()) { Attribute attr = (Attribute) attributes.nextElement(); Enumeration values = attr.getAll(); while (values.hasMoreElements()) { System.out.println(attr.getID() + "=" + values.nextElement()); } } } } catch (NamingException e) { e.printStackTrace(); } } }
connectメソッドで接続し、searchで検索、closeで閉じる。
テストするにはこれぐらいでいい。
searchメソッドの肝は、SearchControlsクラスと、値の取得。
SearchControlsクラスをOBJECT_SCOPEで作成すると、指定のbaseDnのみが検索される。例にあるようにSUBTREE_SCOPEとするとbaseDnとそのサブツリーが検索される。あとよく使いそうなのが、ONELEVEL_SCOPEでbaseDnの直下のエントリのみが検索される。
サンプルとしては、SearchControlsを指定できるようにしたほうがいいかもしれない。
検索語の結果の取得がまたややこしい。例のresultには、検索結果のエントリがすべて格納されるので、それぞれを取り出し(SearchResultクラス)て使用する。
取り出した、SearchResultの中にエントリの属性とその属性値が含まれるのでそれもwhile等でループさせ取得する。
サンプル等を読んでてもすぐにはピンとこなかった。
一応、上記の例の出力結果。
接続成功 loginShell=/bin/bash gecos=testgecos aa gidNumber=2000 uidNumber=2001 uid=namakuwa objectClass=account objectClass=posixAccount objectClass=top homeDirectory=/home/namakuwa cn=namakuwa 接続成功 userPassword=[B@1c29ab2 loginShell=/bin/bash uidNumber=2001 gidNumber=2000 objectClass=account objectClass=posixAccount objectClass=top uid=namakuwa gecos=testgecos aa cn=namakuwa homeDirectory=/home/namakuwa loginShell=/bin/bash gidNumber=2000 uidNumber=2002 userPassword=[B@13a328f uid=kuwagata objectClass=account objectClass=posixAccount objectClass=top homeDirectory=/home/kuwagata cn=kuwagata
追加・削除
追加と、削除は簡単。
package net.kuwalab; import java.util.Enumeration; import java.util.Hashtable; import javax.naming.Context; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; public class LdapTest { private DirContext ctx; public static void main(String[] args) { // LDAP接続 LdapTest lt = new LdapTest(); boolean result = lt.connect("ldap://192.168.0.201", null, "dc=kuwalab,dc=net", "cn=Manager", "test"); if (!result) { System.out.println("接続失敗"); return; } System.out.println("接続成功"); lt.search("ou=user,dc=kuwalab,dc=net", "uid=*"); // エントリの追加 Attributes attrs = new BasicAttributes(); // この辺がJava5 lt.add(attrs, "objectClass", "account", "posixAccount", "top"); lt.add(attrs, "uid", "java"); lt.add(attrs, "cn", "java"); lt.add(attrs, "userPassword", "test"); lt.add(attrs, "loginShell", "/bin/bash"); lt.add(attrs, "uidNumber", "2003"); lt.add(attrs, "gidNumber", "2000"); lt.add(attrs, "homeDirectory", "/home/java"); lt.addEntry("uid=java,ou=user,dc=kuwalab,dc=net", attrs); System.out.println("再検索"); lt.search("ou=user,dc=kuwalab,dc=net", "uid=java"); // 再実行が大変なので削除 lt.deleteEntry("uid=java,ou=user,dc=kuwalab,dc=net"); lt.close(); } public boolean connect(String url, String port, String dn, String id, // 省略 } public void close() { // 省略 } public void search(String baseDn, String filter) { // 省略 } public void add(Attributes attrs, String name, String... values) { Attribute attr = new BasicAttribute(name); for (int i = 0; i < values.length; i++) { attr.add(i, values[i]); } attrs.put(attr); } public void addEntry(String dn, Attributes attrs) { try { // 指定のDNにattrsを追加 ctx.createSubcontext(dn, attrs); } catch (Exception e) { e.printStackTrace(); } } public void deleteEntry(String dn) { try { // 指定のDNの削除 ctx.destroySubcontext(dn); } catch (Exception e) { e.printStackTrace(); } } }
追加は、Attributesに追加するエントリの属性をAttributeとして追加していく。最後にそのAttributesをエントリとして登録する。
上記のaddメソッドは、どちらかというとユーティリティメソッドなので、staticのほうがいい。
削除は、削除するDNを指定するだけ。
結果も省略。追加されているのがわかる。
再検索 loginShell=/bin/bash gidNumber=2000 userPassword=[B@1cd8669 uid=java uidNumber=2003 objectClass=account objectClass=posixAccount objectClass=top homeDirectory=/home/java cn=java
更新
更新はまた独特な形。
上記のクラスに以下のメソッドを追加。
public void modifyAddEntry(List<ModificationItem> modifyList, String name, String value) { Attribute attr = new BasicAttribute(name, value); ModificationItem mod = new ModificationItem( DirContext.ADD_ATTRIBUTE, attr); modifyList.add(mod); } public void modifyReplaceEntry(List<ModificationItem> modifyList, String name, String value) { Attribute attr = new BasicAttribute(name, value); ModificationItem mod = new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attr); modifyList.add(mod); } public void modifyDeleteEntry(List<ModificationItem> modifyList, String name, String value) { Attribute attr = new BasicAttribute(name, value); ModificationItem mod = new ModificationItem( DirContext.REMOVE_ATTRIBUTE, attr); modifyList.add(mod); } public void modify(String dn, List<ModificationItem> modifyList) { try { // 指定のDNの削除 ctx.modifyAttributes(dn, modifyList.toArray(new ModificationItem[modifyList.size()])); } catch (Exception e) { e.printStackTrace(); } }
変更は、ModificationItemの配列が必要となる。今回はListに蓄えて、最後に配列にしている。
変更にも、属性の追加、削除、変更があるが、それは、DirContextのそれぞれADD_ATTRIBUTE、REMOVE_ATTRIBUTE、REPLACE_ATTRIBUTEとなる。
あとは、変更する属性名と、属性値を指定すればいい。
メインは以下。
public static void main(String[] args) { // LDAP接続 LdapTest lt = new LdapTest(); boolean result = lt.connect("ldap://192.168.0.201", null, "dc=kuwalab,dc=net", "uid=namakuwa,ou=user", "test"); if (!result) { System.out.println("接続失敗"); return; } System.out.println("接続成功"); lt.search("uid=namakuwa,ou=user,dc=kuwalab,dc=net", "uid=namakuwa"); lt.close(); result = lt.connect("ldap://192.168.0.201", null, "dc=kuwalab,dc=net", "cn=Manager", "test"); if (!result) { System.out.println("接続失敗"); return; } System.out.println("接続成功"); lt.search("ou=user,dc=kuwalab,dc=net", "uid=*"); // エントリの追加 Attributes attrs = new BasicAttributes(); // この辺がJava5 lt.add(attrs, "objectClass", "account", "posixAccount", "top"); lt.add(attrs, "uid", "java"); lt.add(attrs, "cn", "java"); lt.add(attrs, "userPassword", "test"); lt.add(attrs, "loginShell", "/bin/bash"); lt.add(attrs, "uidNumber", "2003"); lt.add(attrs, "gidNumber", "2000"); lt.add(attrs, "homeDirectory", "/home/java"); lt.addEntry("uid=java,ou=user,dc=kuwalab,dc=net", attrs); System.out.println("再検索"); lt.search("ou=user,dc=kuwalab,dc=net", "uid=java"); // 属性の変更 List<ModificationItem> modifyList = new ArrayList<ModificationItem>(); lt.modifyAddEntry(modifyList, "gecos", "test gecos"); lt.modifyAddEntry(modifyList, "description", "test description"); lt.modifyDeleteEntry(modifyList, "gecos", "test gecos"); lt.modifyReplaceEntry(modifyList, "description", "change test description"); lt.modify("uid=java,ou=user,dc=kuwalab,dc=net", modifyList); System.out.println("再検索"); lt.search("ou=user,dc=kuwalab,dc=net", "uid=java"); // 再実行が大変なので削除 lt.deleteEntry("uid=java,ou=user,dc=kuwalab,dc=net"); lt.close(); }
結果は一部のみ。
再検索 loginShell=/bin/bash gidNumber=2000 userPassword=[B@1cd8669 uid=java uidNumber=2003 objectClass=account objectClass=posixAccount objectClass=top homeDirectory=/home/java cn=java 再検索 userPassword=[B@ca2dce loginShell=/bin/bash uidNumber=2003 gidNumber=2000 objectClass=account objectClass=posixAccount objectClass=top uid=java cn=java homeDirectory=/home/java description=change test description
変更した値が確認できる。
今回で動きは理解できたので、次回はもうちょっとJavaのソースを洗練させてみる。