1、目录服务的操作
我们会用LDAP作为例子来讲解目录服务的操作。与命名服务不同,目录服务的内容上下文的初始化方式需要
改变:
- // Set up the environment for creating the initial context
- Hashtable env = new Hashtable();
- env.put(Context.INITIAL_CONTEXT_FACTORY,
- "com.sun.jndi.ldap.LdapCtxFactory");
- env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
- DirContext ctx = new InitialDirContext(env);
1.1、如何读取属性
你可以使用 DirContext.getAttributes() 方法来读取一个对象的属性,你需要给这个方法传递一个对象的
名字作为参数。例如一个对象在命名系统中的名字是“cn=Ted Geisel, ou=People”,那么我们可以使用如下
的代码得到它的属性:
- Attributes answer = ctx.getAttributes("cn=Ted Geisel, ou=People");
你也可以输出 “answer”来看看:
- for (NamingEnumeration ae = answer.getAll(); ae.hasMore();) {
- Attribute attr = (Attribute)ae.next();
- System.out.println("attribute: " + attr.getID());
- /* Print each value */
- for (NamingEnumeration e = attr.getAll(); e.hasMore();
- System.out.println("value: " + e.next()))
- ;
- }
输出结果是:
# java GetattrsAll
attribute: sn
value: Geisel
attribute: objectclass
value: top
value: person
value: organizationalPerson
value: inetOrgPerson
attribute: jpegphoto
value: [B@1dacd78b
attribute: mail
value: Ted.Geisel@JNDITutorial.com
attribute: facsimiletelephonenumber
value: +1 408 555 2329
attribute: telephonenumber
value: +1 408 555 5252
attribute: cn
value: Ted Geisel
1.1.1、返回需要的属性
有的时候我们只是需要得到一些属性,而不是全部的属性。这样,你可以把属性作为一个数组,把这个数组
作为参数传递给那个方法。
- // Specify the ids of the attributes to return
- String[] attrIDs = {"sn", "telephonenumber", "golfhandicap", "mail"};
- // Get the attributes requested
- Attributes answer = ctx.getAttributes("cn=Ted Geisel, ou=People", attrIDs);
假设我们这个例子里的这个对象,拥有 "sn", "telephonenumber"和"mail"属性,但是没有“golfhandicap”
属性,那么上面的代码返回的结果就应该是:
# java Getattrs
attribute: sn
value: Geisel
attribute: mail
value: Ted.Geisel@JNDITutorial.com
attribute: telephonenumber
value: +1 408 555 5252
1.2、改变属性
DirContext 接口有一些改变对象属性的方法。
1.2.1、批量改变属性
改变属性的方式之一就是批量改变属性,也就是使用许多 ModificationItem 对象来修改属性。
每个 ModificationItem 对象都会有一个常量,来表示对属性进行什么样的操作。这些常量如下:
ADD_ATTRIBUTE
REPLACE_ATTRIBUTE
REMOVE_ATTRIBUTE
对属性的改变会按照队列的顺序来执行,要么所有的改变都生效,要么都不生效。
下面的代码演示了一个例子。它把“mail”这个属性的值,改变成了“geisel@wizards.com”,给“telephonenumber”
属性增加了一个值,并且删除了“jpegphoto”属性。
- // Specify the changes to make
- ModificationItem[] mods = new ModificationItem[3];
- // Replace the "mail" attribute with a new value
- mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
- new BasicAttribute("mail", "geisel@wizards.com"));
- // Add an additional value to "telephonenumber"
- mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE,
- new BasicAttribute("telephonenumber", "+1 555 555 5555"));
- // Remove the "jpegphoto" attribute
- mods[2] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE,
- new BasicAttribute("jpegphoto"));
上面的代码中,我们创建了一个修改属性的 ModificationItem 对象的列表(其实就是一个数组),然后执行
modifyAttributes() 方法来修改属性。
- // Perform the requested modifications on the named object
- ctx.modifyAttributes(name, mods);
1.2.2 只修改某几个属性
你可以不使用上面的方式,而对属性进行某一种操作:
- // Save original attributes
- Attributes orig = ctx.getAttributes(name,
- new String[]{"mail", "telephonenumber", "jpegphoto"});
- 。。。。 。。。
- // Revert changes
- ctx.modifyAttributes(name, DirContext.REPLACE_ATTRIBUTE, orig);
1.3、在目录服务中使用搜索功能
1.3.1、基本的搜索功能
最基本的搜索功能是可以指定一个对象的名字,和一些要搜索的属性的名字。
下面的代码演示了这个功能。我们要进行这么一个搜索:对象必须有“sn”属性,而且数值必须是“Geisel”,
而且必须有“mail”这个属性。
- // Specify the attributes to match
- // Ask for objects that has a surname ("sn") attribute with
- // the value "Geisel" and the "mail" attribute
- Attributes matchAttrs = new BasicAttributes(true); // ignore attribute name case
- matchAttrs.put(new BasicAttribute("sn", "Geisel"));
- matchAttrs.put(new BasicAttribute("mail"));
- // Search for objects that have those matching attributes
- NamingEnumeration answer = ctx.search("ou=People", matchAttrs);
你可以打印出这个结果:
- while (answer.hasMore()) {
- SearchResult sr = (SearchResult)answer.next();
- System.out.println(">>>" + sr.getName());
- printAttrs(sr.getAttributes());
- }
输出结果:
# java SearchRetAll
>>>cn=Ted Geisel
attribute: sn
value: Geisel
attribute: objectclass
value: top
value: person
value: organizationalPerson
value: inetOrgPerson
attribute: jpegphoto
value: [B@1dacd78b
attribute: mail
value: Ted.Geisel@JNDITutorial.com
attribute: facsimiletelephonenumber
value: +1 408 555 2329
attribute: cn
value: Ted Geisel
attribute: telephonenumber
value: +1 408 555 5252
1.3.2、返回指定的属性
上一个例子返回了满足条件的全部属性,当然你也可以只返回需要的属性,这仅仅需要把需要返回的属性,
作为一个数组参数传递给 search() 方法。
- // Specify the ids of the attributes to return
- String[] attrIDs = {"sn", "telephonenumber", "golfhandicap", "mail"};
- // Search for objects that have those matching attributes
- NamingEnumeration answer = ctx.search("ou=People", matchAttrs, attrIDs);
1.4、搜索过滤
在这里,你可以学到一个高级点的搜索方式,就是在搜索中使用过滤。在搜索中实现过滤,我们需要使用
表达式来实现这个功能。下面的这个表达式就表示搜索条件是:对象必须有“sn”属性,而且数值必须是
“Geisel”,而且必须有“mail”这个属性:
(&(sn=Geisel)(mail=*))
下面的例子告诉你如何使用表达式来搜索:
- // Create the default search controls
- SearchControls ctls = new SearchControls();
- // Specify the search filter to match
- // Ask for objects that have the attribute "sn" == "Geisel"
- // and the "mail" attribute
- String filter = "(&(sn=Geisel)(mail=*))";
- // Search for objects using the filter
- NamingEnumeration answer = ctx.search("ou=People", filter, ctls);
下面这个列表有助你使用表达式:
符号 描述
& conjunction (i.e., and -- all in list must be true)
| disjunction (i.e., or -- one or more alternatives must be true)
! negation (i.e., not -- the item being negated must not be true)
= equality (according to the matching rule of the attribute)
~= approximate equality (according to the matching rule of the attribute)
>= greater than (according to the matching rule of the attribute)
<= less than (according to the matching rule of the attribute)
=* presence (i.e., the entry must have the attribute but its value is irrelevant)
* wildcard (indicates zero or more characters can occur in that position);
used when specifying attribute values to match
\ escape (for escaping '*', '(', or ')' when they occur inside an attribute value)
表达式中的每一个项目,都必须使用属性的名字,也可以使用属性的数值。例如“sn=Geisel”意味着属性
“sn”的值为“Geisel”,而 "mail=*" 意味着属性 “mail” 必须存在,但是可以为任意值。
每一个项目必须是在括号中,例如 "(sn=Geisel)"。不同的括号间使用逻辑判断符号来连接起来。
例如 "(| (& (sn=Geisel) (mail=*)) (sn=L*))"。这表示 属性中 sn 必须等于Geisel 并且有 mail 这个
属性 或者 有 sn 这个属性。
详细的内容,请参考 http://www.ietf.org/rfc/rfc2254.txt
当然了,你也可以只返回指定的属性,而不是全部的属性。
- // Specify the ids of the attributes to return
- String[] attrIDs = {"sn", "telephonenumber", "golfhandicap", "mail"};
- SearchControls ctls = new SearchControls();
- ctls.setReturningAttributes(attrIDs);
1.5 搜索控制
在上面的章节中,我们已经看到了一个类:SearchControls,通过这个类,你可以控制搜索的行为。这里
我们就来仔细地看看这个类。
1.5.1 搜索范围
SearchControls 类默认会在整个内容上下文(SearchControls.ONELEVEL_SCOPE)搜索对象,通过设置,你
可以在某一个范围内搜索对象。
1.5.1.1 在一个子树上搜索
通过下面的代码你可以清晰地了解这一功能:
- // Specify the ids of the attributes to return
- String[] attrIDs = {"sn", "telephonenumber", "golfhandicap", "mail"};
- SearchControls ctls = new SearchControls();
- ctls.setReturningAttributes(attrIDs);
- ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
- // Specify the search filter to match
- // Ask for objects that have the attribute "sn" == "Geisel"
- // and the "mail" attribute
- String filter = "(&(sn=Geisel)(mail=*))";
- // Search the subtree for objects by using the filter
- NamingEnumeration answer = ctx.search("", filter, ctls);
1.5.1.2 根据名字来搜索
通过下面的代码你可以清晰地了解这一功能:
- // Specify the ids of the attributes to return
- String[] attrIDs = {"sn", "telephonenumber", "golfhandicap", "mail"};
- SearchControls ctls = new SearchControls();
- ctls.setReturningAttributes(attrIDs);
- ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
- // Specify the search filter to match
- // Ask for objects that have the attribute "sn" == "Geisel"
- // and the "mail" attribute
- String filter = "(&(sn=Geisel)(mail=*))";
- // Search the subtree for objects by using the filter
- NamingEnumeration answer =
- ctx.search("cn=Ted Geisel, ou=People", filter, ctls);
1.5.2 数量的限制
通过下面的代码你可以控制返回结果的数量:
- // Set the search controls to limit the count to 1
- SearchControls ctls = new SearchControls();
- ctls.setCountLimit(1);
1.5.3 时间的限制
如果一个搜索耗费了很长的时间,那可不是一个好方法。这里你可以设置超时的时间。
- // Set the search controls to limit the time to 1 second (1000 ms)
- SearchControls ctls = new SearchControls();
- ctls.setTimeLimit(1000);
参数的单位是毫秒。
如果发生超时现象,那么就会抛出 TimeLimitExceededException。
1.6 结合命名服务和目录服务的操作
我们已经这样的一个概念,就是目录服务是命名服务的一个扩展。例如,之前我们说过命名服务具有 bind(),
rebind(), createSubcontext() 等方法,但是在目录服务里却没有介绍这些方法。
其实目录服务里也有这些方法。下面就用 LDAP 作为例子,介绍一下这些方法。
1.6.1 创建一个具有属性的内容上下文
- import javax.naming.*;
- import javax.naming.directory.*;
- import java.util.Hashtable;
- /**
- * Demonstrates how to create a new subcontext called "ou=Fruits" with some
- * attributes.
- * (Run Destroy after this to remove the subcontext).
- *
- * usage: java Create
- */
- class Create {
- public static void main(String[] args) {
- // Set up the environment for creating the initial context
- Hashtable env = new Hashtable(11);
- env.put(Context.INITIAL_CONTEXT_FACTORY,
- "com.sun.jndi.ldap.LdapCtxFactory");
- env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
- try {
- // Create the initial context
- DirContext ctx = new InitialDirContext(env);
- // Create attributes to be associated with the new context
- Attributes attrs = new BasicAttributes(true); // case-ignore
- Attribute objclass = new BasicAttribute("objectclass");
- objclass.add("top");
- objclass.add("organizationalUnit");
- attrs.put(objclass);
- // Create the context
- Context result = ctx.createSubcontext("ou=Fruits", attrs);
- // Check that it was created by listing its parent
- NamingEnumeration list = ctx.list("");
- // Go through each item in list
- while (list.hasMore()) {
- NameClassPair nc = (NameClassPair)list.next();
- System.out.println(nc);
- }
- // Close the contexts when we're done
- result.close();
- ctx.close();
- } catch (NamingException e) {
- System.out.println("Create failed: " + e);
- }
- }
- }
1.6.2 增加一个具有属性的绑定
- import javax.naming.*;
- import javax.naming.directory.*;
- import java.util.Hashtable;
- /**
- * Demonstrates how to add a binding and its attributes to a context.
- * (Use Rebind example to overwrite binding; use Unbind to remove binding.)
- *
- * usage: java Bind
- */
- class Bind {
- public static void main(String[] args) {
- // Set up the environment for creating the initial context
- Hashtable env = new Hashtable(11);
- env.put(Context.INITIAL_CONTEXT_FACTORY,
- "com.sun.jndi.ldap.LdapCtxFactory");
- env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
- try {
- // Create the initial context
- DirContext ctx = new InitialDirContext(env);
- // Create object to be bound
- Fruit fruit = new Fruit("orange");
- // Create attributes to be associated with object
- Attributes attrs = new BasicAttributes(true); // case-ignore
- Attribute objclass = new BasicAttribute("objectclass");
- objclass.add("top");
- objclass.add("organizationalUnit");
- attrs.put(objclass);
- // Perform bind
- ctx.bind("ou=favorite, ou=Fruits", fruit, attrs);
- // Check that it is bound
- Object obj = ctx.lookup("ou=favorite, ou=Fruits");
- System.out.println(obj);
- // Get its attributes
- Attributes retattrs = ctx.getAttributes("ou=favorite, ou=Fruits");
- GetattrsAll.printAttrs(retattrs);
- // Close the context when we're done
- ctx.close();
- } catch (NamingException e) {
- System.out.println("Operation failed: " + e);
- }
- }
- }
1.6.3 替换一个具有属性的绑定
- import javax.naming.*;
- import javax.naming.directory.*;
- import java.util.Hashtable;
- /**
- * Demonstrates how to replace a binding and its attributes to a context.
- * (Use after Bind example; use Unbind to remove binding.)
- *
- * usage: java Rebind
- */
- class Rebind {
- public static void main(String[] args) {
- // Set up the environment for creating the initial context
- Hashtable env = new Hashtable(11);
- env.put(Context.INITIAL_CONTEXT_FACTORY,
- "com.sun.jndi.ldap.LdapCtxFactory");
- env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
- try {
- // Create the initial context
- DirContext ctx = new InitialDirContext(env);
- // Create object to be bound
- Fruit fruit = new Fruit("lemon");
- // Create attributes to be associated with object
- Attributes attrs = new BasicAttributes(true); // case-ignore
- Attribute objclass = new BasicAttribute("objectclass");
- objclass.add("top");
- objclass.add("organizationalUnit");
- attrs.put(objclass);
- // Perform bind
- ctx.rebind("ou=favorite, ou=Fruits", fruit, attrs);
- // Check that it is bound
- Object obj = ctx.lookup("ou=favorite, ou=Fruits");
- System.out.println(obj);
- // Get its attributes
- Attributes retattrs = ctx.getAttributes("ou=favorite, ou=Fruits");
- GetattrsAll.printAttrs(retattrs);
- // Close the context when we're done
- ctx.close();
- } catch (NamingException e) {
- System.out.println("Operation failed: " + e);
- }
- }
- }