Java提供了JNDI库用于LDAP开发,Spring也提供了Spring-LDAP,用类似hibernateTemplate的原理,使得操作LDAP更加简便。
我们先来学习JNDI操作LDAP的方式:
package com.halo.ldap;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Hashtable;
// 以下是引用jndi相关类
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.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
public class LDAPExample {
// LDAP服务器地址
private final String URL = "ldap://127.0.0.1:389/";
// 改成你的根域名
private final String BASE_DN = "dc=halo,dc=rdc";
// JNDI工厂类
private final String FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
// LDAP认证方式
private final String AUTHENTICATION = "simple";
// 改成你的LDAP管理员帐号
private final String ADMINISTRATOR = "cn=admin,dc=halo,dc=rdc";
// 改成你的管理员密码
private final String PASSWORD = "password";
private final Control[] connCtls = null;
// LDAP上下文
private LdapContext ctx = null;
...
// 建立到LDAP服务器的连接
public void connect() {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, FACTORY);
env.put(Context.PROVIDER_URL, URL + BASE_DN);
env.put(Context.SECURITY_AUTHENTICATION, AUTHENTICATION);
env.put(Context.SECURITY_PRINCIPAL, ADMINISTRATOR);
env.put(Context.SECURITY_CREDENTIALS, PASSWORD);
try {
ctx = new InitialLdapContext(env, connCtls);
System.out.println("LDAP server is connected.");
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 关闭到LDAP服务器的连接
public void disconnect() {
if (null != ctx) {
try {
ctx.close();
System.out.println("LDAP server is disconnected.");
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
…
}
下面展示一个查询LDAP的entry的方法:
/**
* 根据DN(域名)查找一个entry
* @param key DN的名称,例如uid、ou
* @param value DN的值
* @return 返回entry的所有属性(attribute)
*/
public String getDN(String key, String value) {
String dn = "";
if (null == ctx) {
dn = "Not connected yet!";
return dn;
}
SearchControls constraints = new SearchControls();
constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
try {
NamingEnumeration<SearchResult> en = ctx.search("", key + "=" + value, constraints);
while (en != null && en.hasMoreElements()) {
SearchResult result = en.nextElement();
dn += result.getName();
dn += "," + BASE_DN;
Attributes attrs = result.getAttributes();
if (attrs != null) {
NamingEnumeration<? extends Attribute> nes = attrs.getAll();
while (nes.hasMore()) {
Attribute attr = nes.next();
dn += "\n" + attr.getID() + ": " + attr.get();
}
}
}
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return dn;
}
下面展示一个添加LDAP的entry的方法:
public void addPerson() {
Attribute objectClass = new BasicAttribute("objectclass");
String[] objectClassPersons = { "inetOrgPerson", "organizationalPerson", "person", "top" };
Arrays.sort(objectClassPersons);
for (String objectClassPerson : objectClassPersons) {
objectClass.add(objectClassPerson);
}
Attributes attrs = new BasicAttributes(true);
attrs.put(objectClass);
String uid = "Fafa";
attrs.put("uid", uid);
attrs.put("cn", uid);
attrs.put("sn", uid);
attrs.put("displayName", "方方");
attrs.put("mail", "
[email protected]");
attrs.put("description", "方方的登录帐号");
try {
attrs.put("userPassword", "password".getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String userDN = "uid=" + uid + ",ou=People," + BASE_DN;
if (null != ctx) {
try {
ctx.createSubcontext(userDN, attrs);
System.out.println("Person added!");
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
System.out.println("Not connected yet!");
}
}
可见用JNDI操作LDAP还是挺麻烦的,也不便于应用内部的解耦。鉴于此,Spring为我们提供了Spring-LDAP库,使得我们完全不必考虑创建LdapContext连接和关闭,循环NamingEnumeration以读取LDAP属性。我们来看一下使用Spring-LDAP操作的例子:
package com.halo.ldap.dao.impl;
import java.util.List;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
// Spring-LDAP的相关包
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.stereotype.Repository;
import com.halo.ldap.dao.PeopleDao;
import com.halo.ldap.domain.People;
// 本实例结合了注解式的Spring MVC框架,我们可以把操作LDAP的类当作MVC框架的Dao对象
@Repository("peopleDao'")
public class PeopleDaoImpl implements PeopleDao {
// ldapTemplate对象采用类似hibernateTemplate对象的方式,在applicationContext.xml文件中定义,后面我们会帖出applicationContext.xml文件的例子
@Autowired
private LdapTemplate ldapTemplate;
...
}
我们来看几种查询entry的方法,以下是一个遍历查找所有 objectclass=organizationalUnit的entry的方法,方法返回entry的ou属性(attribute)名:
@SuppressWarnings("unchecked")
@Override
public List<String> getAllUnits() {
return ldapTemplate.search("", "(objectclass=organizationalUnit)", new AttributesMapper() {
public Object mapFromAttributes(Attributes attrs) throws javax.naming.NamingException {
return attrs.get("ou").get();
}
});
}
可见用Spring-LDAP查询和操作LDAP的方法要简单很多,完全不必考虑连接LDAP和关闭到LDAP的连接。
以下是一个遍历查找所有 objectclass=person的entry的方法,方法返回Person的对象:
@SuppressWarnings("unchecked")
@Override
public List<People> getAllPeople() {
return ldapTemplate.search("", "(objectclass=person)", new AttributesMapper() {
@Override
public Object mapFromAttributes(Attributes attrs) throws NamingException {
People people = new People();
people.setCn(attrs.get("cn") == null ? null : (String) attrs.get("cn").get());
people.setSn(attrs.get("sn") == null ? null : (String) attrs.get("sn").get());
people.setDescription(
attrs.get("description") == null ? null : (String) attrs.get("description").get());
people.setPhone(attrs.get("phone") == null ? null : (String) attrs.get("phone").get());
return people;
}
});
}
以下是一个根据DN(域名)精确查找entry的方法
@Override
public People findPeople(String dn) {
return (People) ldapTemplate.lookup(dn, new AttributesMapper() {
@Override
public Object mapFromAttributes(Attributes attrs) throws NamingException {
People people = new People();
people.setCn(attrs.get("cn") == null ? null : (String) attrs.get("cn").get());
people.setSn(attrs.get("sn") == null ? null : (String) attrs.get("sn").get());
people.setDescription(
attrs.get("description") == null ? null : (String) attrs.get("description").get());
people.setPhone(attrs.get("phone") == null ? null : (String) attrs.get("phone").get());
return people;
}
});
}
Spring-LDAP配置LDAP连接参数的方式也和Spring + Hibernate的配置方式非常相似,都是在applicationContext.xml文件中配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 与JNDI的参数一样 -->
<bean id="ldapSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="ldap://127.0.0.1:389/" />
<property name="base" value="dc=halo,dc=rdc" />
<property name="userDn" value="cn=admin,dc=halo,dc=rdc" />
<property name="password" value="password" />
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
<constructor-arg ref="ldapSource" />
</bean>
</beans>
注意配置了applicationContext.xml上下文后,就要在web.xml中配置listener来加载:
<!-- Spring上下文配置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Spring-LDAP的jar包在这里下载:http://download.csdn.net/detail/u013668719/9412156
按照Spring-ldap官方网站的说法,还需要在项目中加入以下jar包:
spring-core (miscellaneous utility classes used internally by the framework)
spring-beans (contains interfaces and classes for manipulating Java beans)
commons-logging (a simple logging facade, used internally)
commons-lang (misc utilities, used internally)
以上是jar包的名称,实际jar文件名中还有版本号,例如spring-core的jar文件名是“spring-core-3.2.9.RELEASE.jar”。