一、LDAP概念
LDAP的全称为Lightweight Directory Access Protocol(轻量级目录访问协议), 基于X.500标准, 支持 TCP/IP。
LDAP目录为数据库,通过LDAP服务器(相当于DBMS)处理查询和更新, 以树状的层次结构来存储数据,相对关系型数据库, LDAP主要是优化数据读取的性能,适用于比较少改变、跨平台的信息。
二、Softerra LDAP Administrator
下载安装软件,并配置LDAP
DN:Distinguished Name 唯一标识一条记录的路径,Base DN为基准DN,指定LDAP search的起始DN,即从哪个DN下开始搜索,RDN为叶子结点本身的名字。
DC:Domain Component 一条记录所属区域
OU:Organization Unit 组织单元
CN/UID:一条记录的名字/ID
三、在JAVA中应用LDAP
1、配置文件
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="ldapcontextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="${LdapConnHost}" /> <!—例: LdapConnHost=ldap://10.190.123.123 -->
<property name="userDn" value="${LdapConnUser}" /><!—例: LdapConnUser=cn=root -->
<property name="password" value="${LdapConnPwd}" /><!—例: LdapConnPwd=111111 -->
<property name="base" value="${LdapBaseDn}" /> <!—Base DN 例: LdapBaseDn=DC=FJTIC -->
<property name="pooled" value="false" />
</bean>
2、JAVA代码
1 package com.test.dao; 2 3 import java.beans.PropertyDescriptor; 4 import java.lang.reflect.Field; 5 import java.lang.reflect.InvocationTargetException; 6 import java.lang.reflect.Method; 7 import java.util.ArrayList; 8 import java.util.List; 9 import javax.naming.Name; 10 import javax.naming.NamingEnumeration; 11 import javax.naming.NamingException; 12 import javax.naming.directory.Attribute; 13 import javax.naming.directory.Attributes; 14 import javax.naming.directory.BasicAttribute; 15 import javax.naming.directory.BasicAttributes; 16 import javax.naming.directory.ModificationItem; 17 import org.apache.commons.beanutils.BeanUtils; 18 import org.springframework.beans.factory.annotation.Autowired; 19 import org.springframework.dao.EmptyResultDataAccessException; 20 import org.springframework.ldap.core.ContextMapper; 21 import org.springframework.ldap.core.DirContextAdapter; 22 import org.springframework.ldap.core.DirContextOperations; 23 import org.springframework.ldap.core.DistinguishedName; 24 import org.springframework.ldap.core.LdapTemplate; 25 import org.springframework.ldap.core.support.AbstractContextMapper; 26 import org.springframework.ldap.filter.AndFilter; 27 import org.springframework.ldap.filter.EqualsFilter; 28 import org.springframework.ldap.filter.OrFilter; 29 import org.springframework.stereotype.Component; 30 import com.surekam.model.Person; 31 import com.surekam.utils.DateCl; 32 import com.surekam.utils.StringUtil; 33 34 @Component 35 public class UserDAOL { 36 @Autowired 37 private LdapTemplate ldapTemplate; 38 39 /** 40 * 41 * @Description: 添加 42 * 43 */ 44 public boolean addPerson(Person person) { 45 boolean flag = false; 46 Name dn = buildUDn(person.getUid()); 47 Attributes buildAddAttributes = buildAddAttributes(person); 48 ldapTemplate.bind(dn, null, buildAddAttributes); 49 flag = true; 50 return flag; 51 } 52 53 /** 54 * 55 * @Description: 修改 56 * 57 */ 58 public boolean updatePerson(Person person) { 59 boolean flag = false; 60 Name dn = buildUDn(person.getUid()); 61 ModificationItem[] modificationItem = buildModifyAttributes(person); 62 ldapTemplate.modifyAttributes(dn, modificationItem); 63 flag = true; 64 return flag; 65 } 66 67 /** 68 * 69 * 多条件获取用户 70 * 71 */ 72 public List<Person> getPersonByOu(String ou, String level) { 73 AndFilter filter = new AndFilter(); 74 OrFilter orFilter1 = new OrFilter(); 75 if ("处级干部".equals(level)) { 76 orFilter1.or(new EqualsFilter("cuadministrativelevels", "aa")); 77 orFilter1.or(new EqualsFilter("cuadministrativelevels", "bb")); 78 orFilter1.or(new EqualsFilter("cuadministrativelevels", "cc")); 79 orFilter1.or(new EqualsFilter("cuadministrativelevels", "dd")); 80 } 81 if ("普通员工".equals(level)) { 82 orFilter1.or(new EqualsFilter("cuadministrativelevels", "ee")); 83 orFilter1.or(new EqualsFilter("cuadministrativelevels", "ff")); 84 orFilter1.or(new EqualsFilter("cuadministrativelevels", "gg")); 85 } 86 OrFilter orFilter2 = new OrFilter(); 87 orFilter2.or(new EqualsFilter("departmentnumber", ou)); 88 orFilter2.or(new EqualsFilter("cutransferdnumber", ou)); 89 filter.and(orFilter2); 90 filter.and(orFilter1); 91 System.out.println(filter.toString()); 92 List<Person> list = ldapTemplate.search("cn=users,dc=hq", filter 93 .encode(), new PersonContextMapper()); 94 return list; 95 } 96 97 98 /** 99 * 100 * 生成用户DN 101 * 102 * @return uid=123455,cn=users,dc=hq 103 */ 104 private Name buildUDn(String urdn) { 105 DistinguishedName dn = new DistinguishedName(""); 106 dn.add("dc", "hq"); 107 dn.add("cn", "users"); 108 dn.add("uid", urdn); 109 return dn; 110 } 111 112 /** 113 * 114 * 组织查询结果 115 * 116 */ 117 public static class PersonContextMapper implements ContextMapper { 118 public Object mapFromContext(Object ctx) { 119 if (ctx == null) 120 return new Person(); 121 DirContextAdapter context = (DirContextAdapter) ctx; 122 Person per = new Person(); 123 Attributes attrs = context.getAttributes(); 124 NamingEnumeration results = attrs.getAll(); 125 Class c = per.getClass(); 126 while (results.hasMoreElements()) { 127 try { 128 Attribute attr = (Attribute) results.next(); 129 String value = attr.get().toString(); 130 if (StringUtil.isNotEmpty(value)) { 131 String fieldName = attr.getID(); 132 if ("objectclass".equals(fieldName.toLowerCase())) { 133 continue; 134 } 135 Field field = c.getDeclaredField(fieldName 136 .toLowerCase()); 137 Class fieldClazz = field.getType(); 138 /* 139 * 如果属性条数大于1,那就是多值属性 attr.getAttributeDefinition() 140 * 获取多值属性的方法没找到 141 */ 142 if (fieldClazz.isAssignableFrom(List.class)) { // 属性值数大于1 143 NamingEnumeration values = attr.getAll(); // 获取所有值 144 // LDAP中的多值属性只会是String类型 145 List<String> list = new ArrayList<String>(); 146 while (values.hasMoreElements()) { 147 list.add(values.next().toString()); 148 } 149 BeanUtils.setProperty(per, fieldName.toLowerCase(), 150 list); 151 } else { 152 // 普通属性 153 BeanUtils.setProperty(per, fieldName.toLowerCase(), 154 value); 155 } 156 } 157 } catch (IllegalAccessException e) { 158 e.printStackTrace(); 159 } catch (InvocationTargetException e) { 160 e.printStackTrace(); 161 } catch (NamingException e) { 162 e.printStackTrace(); 163 } catch (SecurityException e) { 164 // TODO Auto-generated catch block 165 e.printStackTrace(); 166 } catch (NoSuchFieldException e) { 167 // TODO Auto-generated catch block 168 e.printStackTrace(); 169 } 170 } 171 per.setDn(context.getNameInNamespace()); 172 return per; 173 } 174 } 175 176 /** 177 * 178 * 组织添加数据数据 179 * 180 */ 181 @SuppressWarnings("unchecked") 182 private Attributes buildAddAttributes(Person p) { 183 Attributes attrs = new BasicAttributes(); 184 BasicAttribute ocattr = new BasicAttribute("objectclass"); 185 ocattr.add("top"); 186 ocattr.add("person"); 187 ocattr.add("organizationalPerson"); 188 ocattr.add("inetOrgPerson"); 189 ocattr.add("FJTicPerson"); 190 attrs.put(ocattr); 191 Class c = p.getClass(); 192 Field[] fields = c.getDeclaredFields(); 193 for (int i = 0; i < fields.length; i++) { 194 try { 195 Class fieldClazz = fields[i].getType(); 196 String fieldName = fields[i].getName(); // 获得属性名 197 String fieldVlue = BeanUtils.getProperty(p, fieldName); // 获得属性值 198 /* 199 * 判断属性是否要过滤,例如修改时间之类的字段LDAP是没有的 判断属性值是否为空,在这里过滤了所有null和"" 200 * 增加操作中不存在主动设置某个值为空的情况 所以只需要处理有值属性 201 */ 202 if (checkfieldName(fieldName) || StringUtil.isEmpty(fieldVlue)) 203 continue; 204 /* 205 * 多值属性的处理 如果多值属性为空,那么增加的时候就不会增加值进去 206 */ 207 if (fieldClazz.isAssignableFrom(List.class)) { // 集合属性 208 BasicAttribute ocattr1 = new BasicAttribute(fieldName); 209 PropertyDescriptor pd = new PropertyDescriptor(fieldName, c); 210 Method getMethod = pd.getReadMethod();// 获得get方法 211 List list = (List) getMethod.invoke(p);// 执行get方法返回一个Object 212 for (Object object : list) { 213 ocattr1.add(object); 214 } 215 attrs.put(ocattr1); 216 } else { 217 attrs.put(fieldName, fieldVlue); 218 } 219 } catch (Exception e) { 220 e.printStackTrace(); 221 } 222 } 223 return attrs; 224 225 } 226 227 /** 228 * 229 * 组织修改数据 230 * 231 */ 232 @SuppressWarnings("unchecked") 233 private ModificationItem[] buildModifyAttributes(Person p) { 234 ArrayList<ModificationItem> attrs = new ArrayList<ModificationItem>(); 235 Class c = p.getClass(); 236 Field[] fields = c.getDeclaredFields(); 237 for (Field field : fields) { 238 try { 239 Class fieldClazz = field.getType(); 240 String fieldName = field.getName(); // 获得属性名 241 String fieldValue = BeanUtils.getProperty(p, fieldName); 242 /* 243 * 判断属性是否要过滤,例如修改时间之类的字段LDAP是没有的 判断属性值是否为空,在这里过滤了所有null和"" 244 * 要置空的属性通过识别特殊属性值:delAtr 在后面做重新置空操作 245 */ 246 if (checkfieldName(fieldName) || StringUtil.isEmpty(fieldValue)) 247 continue; 248 BasicAttribute basicAttr = new BasicAttribute(fieldName); 249 /* 250 * 多值属性的处理 如果传递一个空的list,那么修改的时候就会清空多值属性 (new ArrayList<String>()) 251 */ 252 if (fieldClazz.isAssignableFrom(List.class)) { // 如果是集合属性 253 PropertyDescriptor pd = new PropertyDescriptor(fieldName, c); 254 Method getMethod = pd.getReadMethod();// 获得get方法 255 List list = (List) getMethod.invoke(p);// 执行get方法返回一个Object 256 for (Object object : list) { 257 basicAttr.add(object); 258 } 259 } else { 260 /* 261 * 判断删除标记来对值进行置空 传递过来的对象中有些属性没有做修改就传递了"" 有些是要修改为 "" 262 * 所以在上面要过滤所有 "" 属性,避免将不修改的参数全都置空了 然后通过识别要修改参数的特有值来判断是否主动置空 263 * 如果add一个""进去,那么在LDAP中依然会显示 264 * 如果不给值,由BasicAttribute自动授予空值,那么在LDAP中就不显示了 265 */ 266 if ("delAtr".equals(fieldValue)) { 267 basicAttr.add(""); // 置空属性 268 } else { 269 basicAttr.add(fieldValue);// 有值属性 270 } 271 } 272 // 替换条目 273 attrs.add(new ModificationItem( 274 DirContextAdapter.REPLACE_ATTRIBUTE, basicAttr)); 275 } catch (Exception e) { 276 e.printStackTrace(); 277 } 278 } 279 return attrs.toArray(new ModificationItem[attrs.size()]); 280 } 281 282 /** 283 * 284 * 过滤默认值字段 285 * 286 */ 287 private static boolean checkfieldName(String fieldName) { 288 String[] check = new String[] { "id", "status", "createtime", 289 "updatetime", "dn" }; 290 for (int i = 0; i < check.length; i++) { 291 if (check[i].equalsIgnoreCase(fieldName)) 292 return true; 293 } 294 return false; 295 } 296 }
1 package com.smnpc.util; 2 3 import java.util.Hashtable; 4 import java.util.Vector; 5 6 import javax.naming.Context; 7 import javax.naming.NamingEnumeration; 8 import javax.naming.NamingException; 9 import javax.naming.directory.Attribute; 10 import javax.naming.directory.Attributes; 11 import javax.naming.directory.BasicAttribute; 12 import javax.naming.directory.BasicAttributes; 13 import javax.naming.directory.DirContext; 14 import javax.naming.directory.InitialDirContext; 15 import javax.naming.directory.ModificationItem; 16 import javax.naming.directory.SearchControls; 17 import javax.naming.directory.SearchResult; 18 import javax.naming.ldap.LdapContext; 19 20 /** 21 * Java通过Ldap操作AD的增删该查询 22 * 23 * @author guob 24 */ 25 26 public class LdapbyUser { 27 DirContext dc = null; 28 String root = "dc=example,dc=com"; // LDAP的根节点的DC 29 30 /** 31 * 32 * @param dn类似于"CN=RyanHanson,dc=example,dc=com" 33 * @param employeeID是Ad的一个员工号属性 34 */ 35 public LdapbyUser(String dn, String employeeID) { 36 init(); 37 // add();//添加节点 38 // delete("ou=hi,dc=example,dc=com");//删除"ou=hi,dc=example,dc=com"节点 39 // renameEntry("ou=new,o=neworganization,dc=example,dc=com","ou=neworganizationalUnit,o=neworganization,dc=example,dc=com");//重命名节点"ou=new,o=neworganization,dc=example,dc=com" 40 // searchInformation("dc=example,dc=com", "", 41 // "sAMAccountName=guob");//遍历所有根节点 42 modifyInformation(dn, employeeID);// 修改 43 // Ldapbyuserinfo("guob");//遍历指定节点的分节点 44 close(); 45 } 46 47 /** 48 * 49 * Ldap连接 50 * 51 * @return LdapContext 52 */ 53 public void init() { 54 Hashtable env = new Hashtable(); 55 String LDAP_URL = "ldap://xxxx:389"; // LDAP访问地址 56 String adminName = "example\\user"; // 注意用户名的写法:domain\User或 57 String adminPassword = "userpassword"; // 密码 58 env.put(Context.INITIAL_CONTEXT_FACTORY, 59 "com.sun.jndi.ldap.LdapCtxFactory"); 60 env.put(Context.PROVIDER_URL, LDAP_URL); 61 env.put(Context.SECURITY_AUTHENTICATION, "simple"); 62 env.put(Context.SECURITY_PRINCIPAL, adminName); 63 env.put(Context.SECURITY_CREDENTIALS, adminPassword); 64 try { 65 dc = new InitialDirContext(env);// 初始化上下文 66 System.out.println("认证成功");// 这里可以改成异常抛出。 67 } catch (javax.naming.AuthenticationException e) { 68 System.out.println("认证失败"); 69 } catch (Exception e) { 70 System.out.println("认证出错:" + e); 71 } 72 } 73 74 /** 75 * 添加 76 */ 77 public void add(String newUserName) { 78 try { 79 BasicAttributes attrs = new BasicAttributes(); 80 BasicAttribute objclassSet = new BasicAttribute("objectClass"); 81 objclassSet.add("sAMAccountName"); 82 objclassSet.add("employeeID"); 83 attrs.put(objclassSet); 84 attrs.put("ou", newUserName); 85 dc.createSubcontext("ou=" + newUserName + "," + root, attrs); 86 } catch (Exception e) { 87 e.printStackTrace(); 88 System.out.println("Exception in add():" + e); 89 } 90 } 91 92 /** 93 * 删除 94 * 95 * @param dn 96 */ 97 public void delete(String dn) { 98 try { 99 dc.destroySubcontext(dn); 100 } catch (Exception e) { 101 e.printStackTrace(); 102 System.out.println("Exception in delete():" + e); 103 } 104 } 105 106 /** 107 * 重命名节点 108 * 109 * @param oldDN 110 * @param newDN 111 * @return 112 */ 113 public boolean renameEntry(String oldDN, String newDN) { 114 try { 115 dc.rename(oldDN, newDN); 116 return true; 117 } catch (NamingException ne) { 118 System.err.println("Error: " + ne.getMessage()); 119 return false; 120 } 121 } 122 123 /** 124 * 修改 125 * 126 * @return 127 */ 128 public boolean modifyInformation(String dn, String employeeID) { 129 try { 130 System.out.println("updating...\n"); 131 ModificationItem[] mods = new ModificationItem[1]; 132 /* 修改属性 */ 133 // Attribute attr0 = new BasicAttribute("employeeID", "W20110972"); 134 // mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, 135 // attr0); 136 /* 删除属性 */ 137 // Attribute attr0 = new BasicAttribute("description", 138 // "陈轶"); 139 // mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, 140 // attr0); 141 /* 添加属性 */ 142 Attribute attr0 = new BasicAttribute("employeeID", employeeID); 143 mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr0); 144 /* 修改属性 */ 145 dc.modifyAttributes(dn + ",dc=example,dc=com", mods); 146 return true; 147 } catch (NamingException e) { 148 e.printStackTrace(); 149 System.err.println("Error: " + e.getMessage()); 150 return false; 151 } 152 } 153 154 /** 155 * 关闭Ldap连接 156 */ 157 public void close() { 158 if (dc != null) { 159 try { 160 dc.close(); 161 } catch (NamingException e) { 162 System.out.println("NamingException in close():" + e); 163 } 164 } 165 } 166 167 /** 168 * @param base 169 * :根节点(在这里是"dc=example,dc=com") 170 * @param scope 171 * :搜索范围,分为"base"(本节点),"one"(单层),""(遍历) 172 * @param filter 173 * :指定子节点(格式为"(objectclass=*)",*是指全部,你也可以指定某一特定类型的树节点) 174 */ 175 public void searchInformation(String base, String scope, String filter) { 176 SearchControls sc = new SearchControls(); 177 if (scope.equals("base")) { 178 sc.setSearchScope(SearchControls.OBJECT_SCOPE); 179 } else if (scope.equals("one")) { 180 sc.setSearchScope(SearchControls.ONELEVEL_SCOPE); 181 } else { 182 sc.setSearchScope(SearchControls.SUBTREE_SCOPE); 183 } 184 NamingEnumeration ne = null; 185 try { 186 ne = dc.search(base, filter, sc); 187 // Use the NamingEnumeration object to cycle through 188 // the result set. 189 while (ne.hasMore()) { 190 System.out.println(); 191 SearchResult sr = (SearchResult) ne.next(); 192 String name = sr.getName(); 193 if (base != null && !base.equals("")) { 194 System.out.println("entry: " + name + "," + base); 195 } else { 196 System.out.println("entry: " + name); 197 } 198 199 Attributes at = sr.getAttributes(); 200 NamingEnumeration ane = at.getAll(); 201 while (ane.hasMore()) { 202 Attribute attr = (Attribute) ane.next(); 203 String attrType = attr.getID(); 204 NamingEnumeration values = attr.getAll(); 205 Vector vals = new Vector(); 206 // Another NamingEnumeration object, this time 207 // to iterate through attribute values. 208 while (values.hasMore()) { 209 Object oneVal = values.nextElement(); 210 if (oneVal instanceof String) { 211 System.out.println(attrType + ": " 212 + (String) oneVal); 213 } else { 214 System.out.println(attrType + ": " 215 + new String((byte[]) oneVal)); 216 } 217 } 218 } 219 } 220 } catch (Exception nex) { 221 System.err.println("Error: " + nex.getMessage()); 222 nex.printStackTrace(); 223 } 224 } 225 226 /** 227 * 查询 228 * 229 * @throws NamingException 230 */ 231 public void Ldapbyuserinfo(String userName) { 232 // Create the search controls 233 SearchControls searchCtls = new SearchControls(); 234 // Specify the search scope 235 searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); 236 // specify the LDAP search filter 237 String searchFilter = "sAMAccountName=" + userName; 238 // Specify the Base for the search 搜索域节点 239 String searchBase = "DC=example,DC=COM"; 240 int totalResults = 0; 241 String returnedAtts[] = { "url", "whenChanged", "employeeID", "name", 242 "userPrincipalName", "physicalDeliveryOfficeName", 243 "departmentNumber", "telephoneNumber", "homePhone", "mobile", 244 "department", "sAMAccountName", "whenChanged", "mail" }; // 定制返回属性 245 246 searchCtls.setReturningAttributes(returnedAtts); // 设置返回属性集 247 248 // searchCtls.setReturningAttributes(null); // 不定制属性,将返回所有的属性集 249 250 try { 251 NamingEnumeration answer = dc.search(searchBase, searchFilter, 252 searchCtls); 253 if (answer == null || answer.equals(null)) { 254 System.out.println("answer is null"); 255 } else { 256 System.out.println("answer not null"); 257 } 258 while (answer.hasMoreElements()) { 259 SearchResult sr = (SearchResult) answer.next(); 260 System.out 261 .println("************************************************"); 262 System.out.println("getname=" + sr.getName()); 263 Attributes Attrs = sr.getAttributes(); 264 if (Attrs != null) { 265 try { 266 267 for (NamingEnumeration ne = Attrs.getAll(); ne 268 .hasMore();) { 269 Attribute Attr = (Attribute) ne.next(); 270 System.out.println("AttributeID=" 271 + Attr.getID().toString()); 272 // 读取属性值 273 for (NamingEnumeration e = Attr.getAll(); e 274 .hasMore(); totalResults++) { 275 String user = e.next().toString(); // 接受循环遍历读取的userPrincipalName用户属性 276 System.out.println(user); 277 } 278 // System.out.println(" ---------------"); 279 // // 读取属性值 280 // Enumeration values = Attr.getAll(); 281 // if (values != null) { // 迭代 282 // while (values.hasMoreElements()) { 283 // System.out.println(" 2AttributeValues=" 284 // + values.nextElement()); 285 // } 286 // } 287 // System.out.println(" ---------------"); 288 } 289 } catch (NamingException e) { 290 System.err.println("Throw Exception : " + e); 291 } 292 } 293 } 294 System.out.println("Number: " + totalResults); 295 } catch (Exception e) { 296 e.printStackTrace(); 297 System.err.println("Throw Exception : " + e); 298 } 299 } 300 301 /** 302 * 主函数用于测试 303 * 304 * @param args 305 */ 306 public static void main(String[] args) { 307 new LdapbyUser("CN=RyanHanson", "bbs.it-home.org"); 308 } 309 }