java调用域控服务实现部门用户增删改查
Posted lucy151213
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java调用域控服务实现部门用户增删改查相关的知识,希望对你有一定的参考价值。
原创内容,爬取请指明出处:https://www.cnblogs.com/Lucy151213/p/11005298.html
公司做大后,用到的系统就会越来越多,来个新员工,需要在HR系统添加,然后再到域控系统添加,可能还需要到NC,OA等系统添加账号。后来公司引入总线系统,来个新员工只需要在HR系统添加账号,HR系统把员工信息下发到总线系统,总线系统再下发到其他各个系统,这样能保证数据的一致性。
我负责的那块就是让域控系统接收总线系统下发的员工信息。我在域控服务与总线系统中间搭建一层restful的服务,用于接收解析总线请求,调用相应域控方法。
在开发过程中花最多时间的地方是在证书认证那块,针对修改密码这种包含敏感信息的方法,需用LDAP协议才能实现。
- 域控服务安装
什么是域控服务,参考下面的链接可以对域控有个大概了解:
https://www.cnblogs.com/cnjavahome/p/9029665.html
怎么安装AD证书,我是参考如下文章安装的:
https://blog.csdn.net/hc1017/article/details/81293323
- 证书导出
导出证书步骤也可以参考上面的链接:https://blog.csdn.net/hc1017/article/details/81293323 。需要注意一点是链接里面证书导出选择的是’DER编码二进制X.509(.CER)(D)’,我们这里需要选择’Base64编码X.509(.CER)(S)’,我之前选择DER那种格式就是不成功,换成Base64才成功的(几种格式试出来的o(╥﹏╥)o)。
给导出的证书取名为cert.cer,文件长这样:
执行下面的命令(需把keytool所在目录设置为系统变量,位置在:C:\\Program Files\\Java\\jre1.8.0_102\\bin):
keytool -importcert -keystore F:/yourpath/security.keystore -storepass 123456 -keypass 123456 -alias security -file F:/cert.cer
生成一个叫做security.keystore的文件,将这个文件拷贝到你的Java目录下面,如:C:\\Program Files\\Java\\jre1.8.0_102\\lib\\security
- 中间服务开发
部门或者员工都有编号这个属性,但是域控服务没有这个属性,我把部门编号,员工编号放在域控的属性‘描述’里面,对应的字段叫做‘description’。
全局文件配置如下:
①添加更新部门
Properties env = new Properties(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.SECURITY_AUTHENTICATION, "simple");//LDAP访问安全级别:"none","simple","strong" env.put(Context.SECURITY_PRINCIPAL, ADMINNAME);// AD User env.put(Context.SECURITY_CREDENTIALS, ADMINPASSWORD);// AD Password env.put(Context.PROVIDER_URL, LDAPURL);// LDAP工厂类 LdapContext ctx = null; try ctx = new InitialLdapContext(env, null); SearchControls searchCtls = new SearchControls(); searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); String searchFilter = "(&(objectClass=organizationalUnit)(description=" + departId + "))";//查询这个部门信息 String searchBase = "OU=实际情况,DC=实际情况,DC=com";//在整个公司查找指定description String returnedAtts[] = "name", "description", "distinguishedName";//用于获取组织机构 searchCtls.setReturningAttributes(returnedAtts); boolean isDepartExits = false; NamingEnumeration<SearchResult> answer = ctx.search(searchBase, searchFilter, searchCtls); while (answer.hasMoreElements()) //理论上id是不会重复的,所以这边parentDC只会赋值一次 isDepartExits = true;//如果找到值,那么就表示有这个部门,就更新 SearchResult sr = answer.next(); Attributes Attrs = sr.getAttributes();//得到符合条件的属性集 if (Attrs != null) for (NamingEnumeration ne = Attrs.getAll(); ne.hasMore(); ) Attribute Attr = (Attribute) ne.next();//得到下一个属性 if ("distinguishedName".equals(Attr.getID())) for (NamingEnumeration e = Attr.getAll(); e.hasMore(); ) String userInfo = e.next().toString(); departDC = userInfo; System.out.print(userInfo); System.out.println(""); if (isDepartExits) //这个部门id存在,那么如果传入的部门名称不相同,就更新部门名称,相同就不做处理 if (!isNullOrEmpty(departDC)) String[] dcs = departDC.split(","); if (dcs.length > 0) //名称不相同表示修改了部门名称,那么就修改 if (!((dcs[0].substring(dcs[0].indexOf("=") + 1)).equals(departName))) String newDc = "OU=" + departName + departDC.substring(departDC.indexOf(","), departDC.length()); ctx.rename(departDC, newDc); retMsg.setSuccess(true); retMsg.setMsg("更新成功"); retMsg.setUid(departId); logger.info("更新部门成功。 departId:"+departId+" departDC:"+departDC); else retMsg.setSuccess(true); retMsg.setMsg("部门名字未做修改,不处理"); retMsg.setUid(departId); logger.info("部门名称未做修改,不予更新。 departId:"+departId+" departDC:"+departDC); else //添加新部门,首先检查父id是否有效 String searchFilter1 = "(&(objectClass=organizationalUnit)(description=" + parentId + "))";//查询父id信息 NamingEnumeration<SearchResult> answer1 = ctx.search(searchBase, searchFilter1, searchCtls); while (answer1.hasMoreElements()) //理论上id是不会重复的,所以这边parentDC只会赋值一次 SearchResult sr = answer1.next(); Attributes Attrs = sr.getAttributes();//得到符合条件的属性集 if (Attrs != null) for (NamingEnumeration ne = Attrs.getAll(); ne.hasMore(); ) Attribute Attr = (Attribute) ne.next();//得到下一个属性 if ("distinguishedName".equals(Attr.getID())) for (NamingEnumeration e = Attr.getAll(); e.hasMore(); ) String userInfo = e.next().toString(); parentDC = userInfo; if (isNullOrEmpty(parentDC)) logger.error("父组织的id不存在。 departId:"+departId); return getNoParamDataMessage("父组织的id不存在"); else ldapGroupDN = "OU=" + departName + "," + parentDC; //查询这个departId是否存在 try Attributes attrs = ctx.getAttributes(parentDC);//note...... NamingEnumeration<String> nEnum = attrs.getIDs(); //如果没有报错,说明有这个组织的dn,那么如果明知不相同 Attributes attrs1 = new BasicAttributes(); attrs1.put("objectClass", "organizationalunit"); attrs1.put("description", departId);//部门的id放在description下面 ctx.createSubcontext(ldapGroupDN, attrs1); retMsg.setSuccess(true); retMsg.setMsg("添加成功"); retMsg.setUid(departId); logger.info("部门添加成功。 departId:"+departId+" departDC:"+ldapGroupDN); catch (Exception ex) logger.error("添加部门发生异常。departId:"+departId+" departDC:"+ldapGroupDN+" 。ex:"+ex.getMessage()); logger.error("stack:"+ex.getStackTrace()); ctx.close(); catch (NamingException e) logger.error("添加或者更新部门发生异常。departId:"+departId+" stack:"+e.getStackTrace()); retMsg.setUid(departId); retMsg.setSuccess(false); retMsg.setMsg("失败"); ctx.close();
②删除部门
和上面的代码类似,在获取到departDC后执行:ctx.destroySubcontext(departDC);
③添加更新用户
之前写的挫,每次添加更新我就创建一次连接,有一次传很多用户过来,然后速度就很慢,所有把创建连接提出来。
循环调用添加/更新用户方法:
LdapContext ctx = null; LdapContext sslCtx = null; try Properties env = new Properties(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.SECURITY_AUTHENTICATION, "simple");//LDAP访问安全级别:"none","simple","strong" env.put(Context.SECURITY_PRINCIPAL, ADMINNAME);// AD User env.put(Context.SECURITY_CREDENTIALS, ADMINPASSWORD);// AD Password env.put(Context.PROVIDER_URL, LDAPURL);// LDAP工厂类 ctx = new InitialLdapContext(env, null); sslCtx = getSslConn(); for (UserEntity u : users) dataMessageList.add(ADHelpUtil.addUserNew(u,ctx,sslCtx)); ctx.close(); sslCtx.close(); catch (Exception ex) logger.error("新方法添加用户异常。ex:"+ex.getMessage()); try ctx.close(); sslCtx.close(); catch (NamingException e) e.printStackTrace();
获取SSL连接代码如下:
Properties env = new Properties(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.SECURITY_AUTHENTICATION, "simple");//LDAP访问安全级别:"none","simple","strong" env.put(Context.SECURITY_PRINCIPAL, ADMINNAME);// AD User env.put(Context.SECURITY_CREDENTIALS, ADMINPASSWORD);// AD Password env.put(Context.SECURITY_PROTOCOL, "ssl"); env.put(Context.PROVIDER_URL, LDAPSURL);// LDAP工厂类 System.setProperty("javax.net.ssl.trustStore", TRUST_STORE_PATH); System.setProperty("javax.net.ssl.trustStorePassword", TRUST_STORE_PASSWORD); LdapContext ctx = null; try ctx = new InitialLdapContext(env, null); return ctx; catch (Exception ex) System.out.println("Problem resetting password: " + ex); ex.printStackTrace();
添加方法代码片段如下:
try SearchControls searchCtls = new SearchControls(); searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); String searchFilter = "(&(objectClass=user)(description=" + uid + "))";//查询这个用户信息 String searchBase = "OU=实际情况,DC=实际情况,DC=com";//在整个公司查找指定description String returnedAtts[] = "name", "description", "distinguishedName";//用于获取组织机构 searchCtls.setReturningAttributes(returnedAtts); boolean isUserExits = false; NamingEnumeration<SearchResult> answer = ctx.search(searchBase, searchFilter, searchCtls); if(answer.hasMoreElements())//理论上id是不会重复的,所以这边usertDC只会赋值一次 isUserExits = true;//如果找到值,那么就表示有这个部门,就更新 userDC = getDistinguishedName(answer); //检测部门编号查询部门是否存在,不存在抛挫,代码省略 //检测传入的员工经理是否有效,无需抛挫,代码省略 if (isUserExits) //用户存在,如果名或姓或部门id有变化,就修改 if (!isNullOrEmpty(userDC)) //如果用户cancel=1,就直接停用,不用去修改其他属性 if("1".equals(canceld))//启用停用需要使用ssl协议 ModificationItem[] mods = new ModificationItem[1]; mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("userAccountControl", Integer.toString(DONT_EXPIRE_PASSWORD+UF_NORMAL_ACCOUNT+ACCOUNTDISABLE))); if(null!=sslCtx) sslCtx.modifyAttributes(userDC, mods); logger.error("禁用客户成功 userid:"+uid+" userDC:"+userDC); else logger.error("禁用客户失败,SSL失败 userid:"+uid+" userDC:"+userDC); return getNoParamDataMessage("禁用客户失败,SSL失败"); mods = null; else String curDepDC = userDC.substring(userDC.indexOf(",") + 1, userDC.length()); String newDc = userDC; if (!curDepDC.equals(departDC)) //说明用户是要修改所在部门 newDc = userDC.substring(0, userDC.indexOf(",")) + "," + departDC; ctx.rename(userDC, newDc);//换部门 //修改员工属性 //设置属性 Attributes attrs = new BasicAttributes(); attrs.put("displayName", lastName + firstName); attrs.put("description", uid); attrs.put("mail", email); attrs.put("userPrincipalName", email); if(null!=company&&!"".equals(company)) attrs.put("company", company); if(null!=title&&!"".equals(title)) attrs.put("title", title); if(null!=officePhone&&!"".equals(officePhone)) attrs.put("telephoneNumber", officePhone); if(null!=mobile&&!"".equals(mobile)) attrs.put("mobile", mobile); if(!isNullOrEmpty(managerDC)) attrs.put("manager",managerDC); if(departDC.length()!=0) int ind1 = departDC.indexOf("="); int ind2 = departDC.indexOf(","); if(ind1!=-1 && ind2!=-1) attrs.put(new BasicAttribute("department", departDC.substring(ind1+1,ind2))); ctx.modifyAttributes(newDc, DirContext.REPLACE_ATTRIBUTE, attrs); else //添加新用户 Attribute objclass = new BasicAttribute("objectclass"); objclass.add("top"); objclass.add("user"); Attributes attrs1 = new BasicAttributes(); attrs1.put(objclass); attrs1.put(new BasicAttribute("givenname", firstName)); attrs1.put(new BasicAttribute("sn", lastName)); attrs1.put(new BasicAttribute("displayName", lastName + firstName)); attrs1.put(new BasicAttribute("description", uid)); attrs1.put(new BasicAttribute("mail", email)); if(departDC.length()!=0) int ind1 = departDC.indexOf("="); int ind2 = departDC.indexOf(","); if(ind1!=-1 && ind2!=-1) attrs1.put(new BasicAttribute("department", departDC.substring(ind1+1,ind2))); attrs1.put(new BasicAttribute("sAMAccountName", email.substring(0, email.indexOf("@")))); attrs1.put(new BasicAttribute("userPrincipalName", email)); if(null!=company && !"".equals(company)) attrs1.put(new BasicAttribute("company", company)); if(null!=title && !"".equals(title)) attrs1.put(new BasicAttribute("title", title)); if(null!=officePhone && !"".equals(officePhone)) attrs1.put(new BasicAttribute("telephoneNumber", officePhone)); if(null!=mobile && !"".equals(mobile)) attrs1.put(new BasicAttribute("mobile", mobile)); if(!isNullOrEmpty(managerDC)) attrs1.put(new BasicAttribute("manager",managerDC)); String newDc = "CN=" + lastName + firstName + "," + departDC;//新用户的dn ctx.createSubcontext(newDc,attrs1); ModificationItem[] mods = new ModificationItem[2]; String newQuotedPassword = "\\"" + COMMON_PASSWORD + "\\""; byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE"); mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword)); mods[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("userAccountControl", Integer.toString(UF_NORMAL_ACCOUNT + UF_PASSWORD_EXPIRED + UF_PASSWD_NOTREQD))); if(null!=sslCtx) sslCtx.modifyAttributes(newDc, mods); mods = null; catch (NameAlreadyBoundException ex) logger.error("异常:uid:"+uid+" userDC:"+userDC+" ex:"+ex.getMessage()); catch (Exception e) logger.error("异常:uid:"+uid+" userDC:"+userDC+" ex:"+e.getMessage());
④删除用户
核心代码,查询到userDC后,执行:ctx.destroySubcontext(userDC);
⑤获取员工列表(传入上级部门编号)
与上面的代码雷同,省略
⑥获取部门列表(传入上级部门编号)
与上面的代码雷同,省略
以上是关于java调用域控服务实现部门用户增删改查的主要内容,如果未能解决你的问题,请参考以下文章