将Ldap组织结构及用户信息同步到MySQL,用Spring Boot项目操作
从上一篇《将Mybatis引入Spring Boot项目连接数据库操作》知道了如何在Spring Boot项目操作数据库,学会了增删查改基本操作方法。本节记录如何从Ldap获取组织结构及用户信息并导入数据库。
一,引入Maven依赖并设置ldap连接信息
首先在pom.xml添加引入ldap依赖,如下所示:
<!-- LDAP --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-ldap</artifactId> </dependency>
保存后等待自动加载插件,加载完成后在application.properties配置文件中写目标ldap连接信息:
# 目标ldap地址 spring.ldap.urls=ldap://192.168.10.168:389 # base是Base DN 即根节点 spring.ldap.base=DC=ldap,DC=local # 指定用户的username是User DN即distinguishedName,可分辨名称、标识名称 spring.ldap.username=CN=Administrator,CN=Users,DC=ldap,DC=local # 指定用户的密码 spring.ldap.password=这里写密码
上面是示例。如果不确定信息是否正确,可以用LDAPSoft Ldap Browser,AdminLdap等客户端工具连接测试。能正确加载组织结构及用户信息代表连接信息正确。保存配置。
二,从ldap获取数据的基础方法,用户及部门实体模型
在项目中新建package包,为了容易区分取名“LdapDemo”,在里面新建3个Class类,分别取名LdapPerson,LdapDepartment,LdapService。前两个是ldap用户信息实体模型和ldap部门信息实体模型,第3个是写获取数据等基础方法的Service。
LdapPerson.java代码如下:
package xxh.springbootmvc.xxhdemo1.LdapDemo; public class LdapPerson { public String getObjectClass() { return objectClass; } public void setObjectClass(String objectClass) { this.objectClass = objectClass; } public String getCn() { return cn; } public void setCn(String cn) { this.cn = cn; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public String getsAMAccountName() { return sAMAccountName; } public void setsAMAccountName(String sAMAccountName) { this.sAMAccountName = sAMAccountName; } public String getSn() { return sn; } public void setSn(String sn) { this.sn = sn; } public String getGivenName() { return givenName; } public void setGivenName(String givenName) { this.givenName = givenName; } public String getObjectGUID() { return objectGUID; } public void setObjectGUID(String objectGUID) { this.objectGUID = objectGUID; } public boolean isDeleted() { return isDeleted; } public void setDeleted(boolean deleted) { isDeleted = deleted; } public boolean isPrivilegeHolder() { return isPrivilegeHolder; } public void setPrivilegeHolder(boolean privilegeHolder) { isPrivilegeHolder = privilegeHolder; } public boolean isRecycled() { return isRecycled; } public void setRecycled(boolean recycled) { isRecycled = recycled; } public String getDistinguishedName() { return distinguishedName; } public void setDistinguishedName(String distinguishedName) { this.distinguishedName = distinguishedName; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } private String objectClass; // organizationalPerson private String cn; // 名称、唯一标识 private String displayName; // 显示名 private String sAMAccountName; // 登录名(唯一标识) private String sn; // 姓 private String givenName; // 名 private String objectGUID; private boolean isDeleted; // 删除的、禁用的 private boolean isPrivilegeHolder; // 特权 private boolean isRecycled; // 恢复的 private String distinguishedName; // DN private String description; }
保存。
LdapDepartment.java代码如下:
package xxh.springbootmvc.xxhdemo1.LdapDemo; public class LdapDepartment { public String getObjectClass() { return objectClass; } public void setObjectClass(String objectClass) { this.objectClass = objectClass; } public String getOu() { return ou; } public void setOu(String ou) { this.ou = ou; } public String getObjectGUID() { return objectGUID; } public void setObjectGUID(String objectGUID) { this.objectGUID = objectGUID; } public boolean isDeleted() { return isDeleted; } public void setDeleted(boolean deleted) { isDeleted = deleted; } public boolean isPrivilegeHolder() { return isPrivilegeHolder; } public void setPrivilegeHolder(boolean privilegeHolder) { isPrivilegeHolder = privilegeHolder; } public boolean isRecycled() { return isRecycled; } public void setRecycled(boolean recycled) { isRecycled = recycled; } public String getDistinguishedName() { return distinguishedName; } public void setDistinguishedName(String distinguishedName) { this.distinguishedName = distinguishedName; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } private String objectClass; // organizationalUnit private String ou; //名称、唯一标识 private String objectGUID; private boolean isDeleted; // 删除的、禁用的 private boolean isPrivilegeHolder; // 特权 private boolean isRecycled; // 恢复的 private String distinguishedName; // DN private String description; }
LdapService.java代码如下:
package xxh.springbootmvc.xxhdemo1.LdapDemo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.ldap.core.AttributesMapper; import org.springframework.ldap.core.LdapTemplate; import org.springframework.stereotype.Service; import javax.naming.NamingException; import javax.naming.directory.Attributes; import java.io.*; import java.util.List; import static org.springframework.ldap.query.LdapQueryBuilder.query; @Service public class LdapService { @Autowired private LdapTemplate ldapTemplate; // 获取Ldap部门 public List<LdapDepartment> getLdapDepartment(String objectClass,String DN) { if (null == objectClass || objectClass.length() < 1) objectClass = "organizationalUnit"; System.out.println("getLdapDepartment> " + objectClass + "," + DN); ldapTemplate.setIgnorePartialResultException(true); return ldapTemplate.search( query().where("objectclass").is(objectClass) .and("distinguishedName").like(DN), new LdapDepartmentAttributesMapper()); } // 自定义序列化Ldap部门实体 private class LdapDepartmentAttributesMapper implements AttributesMapper<LdapDepartment> { @Override public LdapDepartment mapFromAttributes(Attributes attrs) throws NamingException { LdapDepartment ldapDepartment = new LdapDepartment(); if (null != attrs.get("objectClass")) { ldapDepartment.setObjectClass((String) attrs.get("objectClass").get()); } if (null != attrs.get("ou")) { ldapDepartment.setOu((String) attrs.get("ou").get()); } if (null != attrs.get("objectGUID")) { // 将ldap的objectGUID转换成字符串 ldapDepartment.setObjectGUID(convertObjectGUID(attrs.get("objectGUID").get())); } if (null != attrs.get("isDeleted")) { ldapDepartment.setDeleted((boolean) attrs.get("isDeleted").get()); } if (null != attrs.get("isPrivilegeHolder")) { ldapDepartment.setPrivilegeHolder((boolean) attrs.get("isPrivilegeHolder").get()); } if (null != attrs.get("isRecycled")) { ldapDepartment.setRecycled((boolean) attrs.get("isRecycled").get()); } if (null != attrs.get("distinguishedName")) { ldapDepartment.setDistinguishedName((String) attrs.get("distinguishedName").get()); } if (null != attrs.get("description")) { ldapDepartment.setDescription((String) attrs.get("description").get()); } return ldapDepartment; } } // 获取Ldap用户 public List<LdapPerson> getLdapPerson(String objectClass,String DN) { if(null==objectClass || objectClass.length()<1) objectClass="organizationalPerson"; ldapTemplate.setIgnorePartialResultException(true); return ldapTemplate.search( query().where("objectclass").is(objectClass) .and("distinguishedName").is(DN), new LdapPersonAttributesMapper()); } // 自定义序列化Ldap用户实体 private class LdapPersonAttributesMapper implements AttributesMapper<LdapPerson> { @Override public LdapPerson mapFromAttributes(Attributes attrs) throws NamingException { LdapPerson ldapPerson = new LdapPerson(); if (null != attrs.get("objectClass")) { ldapPerson.setObjectClass((String) attrs.get("objectClass").get()); } if (null != attrs.get("cn")) { ldapPerson.setCn((String) attrs.get("cn").get()); } if (null != attrs.get("displayName")) { ldapPerson.setDisplayName((String) attrs.get("displayName").get()); } if (null != attrs.get("sAMAccountName")) { ldapPerson.setsAMAccountName((String) attrs.get("sAMAccountName").get()); } if (null != attrs.get("sn")) { ldapPerson.setSn((String) attrs.get("sn").get()); } if (null != attrs.get("givenName")) { ldapPerson.setGivenName((String) attrs.get("givenName").get()); } if (null != attrs.get("objectGUID")) { // 将ldap的objectGUID转换成字符串 ldapPerson.setObjectGUID(convertObjectGUID(attrs.get("objectGUID").get())); } if (null != attrs.get("isDeleted")) { ldapPerson.setDeleted((boolean) attrs.get("isDeleted").get()); } if (null != attrs.get("isPrivilegeHolder")) { ldapPerson.setPrivilegeHolder((boolean) attrs.get("isPrivilegeHolder").get()); } if (null != attrs.get("isRecycled")) { ldapPerson.setRecycled((boolean) attrs.get("isRecycled").get()); } if (null != attrs.get("distinguishedName")) { ldapPerson.setDistinguishedName((String) attrs.get("distinguishedName").get()); } if (null != attrs.get("description")) { ldapPerson.setDescription((String) attrs.get("description").get()); } return ldapPerson; } } // 示例:获取全部用户名 public List<String> getAllPersonNames() { ldapTemplate.setIgnorePartialResultException(true); return ldapTemplate.search( query().where("objectclass").is("person"), (AttributesMapper<String>) attrs -> (String) attrs.get("cn").get()); } //region 这里都是解决ldap获取数据中的objectGUID乱码问题,虽然解决了乱码但是值不一样,该值不建议当做标识使用。 /** * 对象转数组 * @param obj * @return */ private static byte[] toByteArray (Object obj) { byte[] bytes = null; ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(obj); oos.flush(); bytes = bos.toByteArray (); oos.close(); bos.close(); } catch (IOException ex) { ex.printStackTrace(); } return bytes; } /** * 数组转对象 * @param bytes * @return */ private static Object toObject (byte[] bytes) { Object obj = null; try { ByteArrayInputStream bis = new ByteArrayInputStream (bytes); ObjectInputStream ois = new ObjectInputStream (bis); obj = ois.readObject(); ois.close(); bis.close(); } catch (IOException ex) { ex.printStackTrace(); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } return obj; } private static String AddLeadingZero(int k) { return (k <= 0xF) ? "0" + Integer.toHexString(k) : Integer.toHexString(k); } private static String convertObjectGUID(Object ObjectGUID) { byte[] GUID = toByteArray(ObjectGUID); String strGUID = ""; String byteGUID = ""; //Convert the GUID into string using the byte format for (int c = 0; c < GUID.length; c++) { byteGUID = byteGUID + "\\" + AddLeadingZero((int) GUID[c] & 0xFF); } // strGUID = "{"; strGUID = strGUID + AddLeadingZero((int) GUID[3] & 0xFF); strGUID = strGUID + AddLeadingZero((int) GUID[2] & 0xFF); strGUID = strGUID + AddLeadingZero((int) GUID[1] & 0xFF); strGUID = strGUID + AddLeadingZero((int) GUID[0] & 0xFF); strGUID = strGUID + "-"; strGUID = strGUID + AddLeadingZero((int) GUID[5] & 0xFF); strGUID = strGUID + AddLeadingZero((int) GUID[4] & 0xFF); strGUID = strGUID + "-"; strGUID = strGUID + AddLeadingZero((int) GUID[7] & 0xFF); strGUID = strGUID + AddLeadingZero((int) GUID[6] & 0xFF); strGUID = strGUID + "-"; strGUID = strGUID + AddLeadingZero((int) GUID[8] & 0xFF); strGUID = strGUID + AddLeadingZero((int) GUID[9] & 0xFF); strGUID = strGUID + "-"; strGUID = strGUID + AddLeadingZero((int) GUID[10] & 0xFF); strGUID = strGUID + AddLeadingZero((int) GUID[11] & 0xFF); strGUID = strGUID + AddLeadingZero((int) GUID[12] & 0xFF); strGUID = strGUID + AddLeadingZero((int) GUID[13] & 0xFF); strGUID = strGUID + AddLeadingZero((int) GUID[14] & 0xFF); strGUID = strGUID + AddLeadingZero((int) GUID[15] & 0xFF); // strGUID = strGUID + "}"; // System.out.println("GUID (String format): " + strGUID); // System.out.println("GUID (Byte format): " + byteGUID); return strGUID; } //endregion }
Service里面方法实现可以参考下面几篇博客:
https://www.jianshu.com/p/77517e26a357
https://blog.csdn.net/a118170653/article/details/43449331
注意:
1,LdapService类前面要加@Service标注。
2,需要在类体声明LdapTemplate才能在方法里使用(在方法里面声明不行)。
@Autowired
private LdapTemplate ldapTemplate;
3,查询ldap组织结构及用户信息需要使用拉姆达表达式,具体请看上面代码示例。
三,调用LdapService获取数据并以接口形式输出显示
在项目里面创建Class类,命名为ldapTestController,在这里面写接口调用LdapService获取数据并以接口形式输出显示。如下:
package xxh.springbootmvc.xxhdemo1.LdapDemo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @EnableAutoConfiguration @RequestMapping("/v1/ldapdemo") public class ldapTestController { /* 声明LdapService */ @Autowired private LdapService ldapService; // http://localhost:8888/v1/ldapdemo/getPersonNameList /* 获取所有用户名称接口 */ @RequestMapping("/getPersonNameList") public Object queryPersonNameList() { return ldapService.getAllPersonNames(); } // http://localhost:8888/v1/ldapdemo/getLdapDepartmentList?objectclass=&dn= // http://localhost:8888/v1/ldapdemo/getLdapDepartmentList?objectclass=&dn= // http://localhost:8888/v1/ldapdemo/getLdapDepartmentList?dn=OU=%E8%BF%90%E8%90%A5%E6%9C%8D%E5%8A%A1%E9%83%A8,DC=ldap,DC=local /* 根据筛选获条件取部门接口 */ @RequestMapping("/getLdapDepartmentList") public Object queryLdapDepartmentList(String objectclass, String dn) { if (null == dn || dn.length() < 1) dn = "OU=实施组,OU=运营服务部,DC=ldap,DC=local"; return ldapService.getLdapDepartment(objectclass, dn); } // http://localhost:8888/v1/ldapdemo/getLdapPersonList?dn=OU=实施组,OU=运营服务部,DC=ldap,DC=local /* 根据筛选获条件取用户接口 */ @RequestMapping("/getLdapPersonList") public Object queryLdapPersonList(String objectclass, String dn) { if (null == dn || dn.length() < 1) dn = "CN=刘备,OU=实施组,OU=运营服务部,DC=ldap,DC=local"; return ldapService.getLdapPerson(objectclass, dn); } }
我这里是直接用接口显示出来。其实真实项目中是录入到数据库里面。
本篇博客涉及到的项目文件及目录结构:
四,接口效果截图
获取所有用户名称接口 效果如下:
根据筛选获条件取部门接口 效果如下:
根据筛选获条件取用户接口 效果如下:
本篇总结就写到这里了。
缺点:
1,虽然将Ldap的objectGUID能转换成字符串不再是乱码,但是值不等于Ldap里面的objectGUID值。所以不建议用它作为数据对比的标识。
2,本篇代码还不能按照友好的上下级关系一层一层往下查找(目前是查出所有的部门或用户,或者筛选已知部门或已知用户)。
上一篇:将Mybatis引入Spring Boot项目连接数据库操作
下一篇: