一、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、spring配置文件
<bean />
</bean>
<bean >
<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 }