package com.devops.devopsauth.ad.util; import com.devops.devopscommon.exception.ServerException; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.naming.Context; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.*; import javax.naming.ldap.InitialLdapContext; import java.util.Hashtable; /** * 工具类,处理AD相关的用户操作 * 并置为final不允许继承变更 **/ @Slf4j @Component public final class ADUserUtil { private DirContext dc = null; @Value("${devops.app.ad.root}") private String root; @Value("${devops.app.ad.adminName}") private String adminName; @Value("${devops.app.ad.adminPassword}") private String adminPassword; @Value("${devops.app.ad.ldapURL}") private String ldapURL; @Value("${devops.app.ad.keystore}") private String keystore; public void getDirContext() throws ServerException { // ladp的一些配置 Hashtable env = new Hashtable(); log.info(keystore); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_PRINCIPAL, adminName); env.put(Context.SECURITY_CREDENTIALS, adminPassword); env.put(Context.SECURITY_PROTOCOL, "ssl"); env.put(Context.PROVIDER_URL, ldapURL); try { // 初始化ldapcontext dc = new InitialLdapContext(env, null); }catch (Exception e) { log.error("AD域服务连接认证失败",e); throw new ServerException("AD域名连接认证失败!"); } } /** * @Description:关闭AD域服务连接 * @author moonxy * @date 2018-05-15 */ public void close() throws ServerException { if (dc != null) { try { dc.close(); } catch (NamingException e) { log.error("NamingException in close():", e); throw new ServerException("关闭AD服务连接失败",e); } } } /** * 新增AD用户 * @param newUserName - 对应userCode * @param displayName -实际姓名 * @param password - 密码 * @throws ServerException */ public void add(String newUserName, String displayName, String password) throws ServerException { try { Attributes attrs = new BasicAttributes(true); attrs.put("objectClass", "user"); attrs.put("samAccountName", newUserName); attrs.put("displayName", displayName); attrs.put("userAccountControl","512"); final byte[] quotedPasswordBytes = ('"'+password+'"').getBytes("UTF-16LE"); attrs.put(new BasicAttribute("unicodePwd", quotedPasswordBytes)); attrs.put("userPassword",password); attrs.put("userPrincipalName", newUserName + "@stosystem.com"); attrs.put("cn", newUserName); attrs.put("sn", newUserName); dc.createSubcontext("CN=" + newUserName + "," + root, attrs); log.info("新增AD域用户成功:" + newUserName); } catch (Exception e) { log.info("新增AD域用户失败:" + newUserName,e); throw new ServerException("新增AD用户失败"); } } /** * @Description:删除AD域用户 * @author moonxy * @date 2018-05-15 */ public void delete(String userName) throws ServerException { try { dc.destroySubcontext("CN="+ userName + ","+ root); log.info("删除AD域用户成功:" + userName); } catch (Exception e) { log.error("删除AD域用户失败:" + userName,e); throw new ServerException("删除AD域用户失败:"+userName); } } /** * @Description:重命名AD域用户 * @author moonxy * @date 2018-05-15 */ public boolean renameEntry(String oldDN, String newDN) { try { dc.rename(oldDN, newDN); log.info("重命名AD域用户成功"); return true; } catch (NamingException ne) { log.error("重命名AD域用户失败",ne); return false; } } /** * 修改AD用户属性 * @param dn * @param fieldValue * @param type -add/remove/replace 新增/删除/覆盖 * @return */ public boolean modifyInformation(String dn, String fieldValue, String type) { try { ModificationItem[] mods = new ModificationItem[1]; // 修改属性 Attribute attr0 = new BasicAttribute("homePhone",fieldValue); mods[0] = new ModificationItem("add".equals(type)? DirContext.ADD_ATTRIBUTE :("remove".equals(type) ? DirContext.REMOVE_ATTRIBUTE : DirContext.REPLACE_ATTRIBUTE), attr0); dc.modifyAttributes(dn + "," + root, mods); log.info("修改AD域用户属性成功"); return true; } catch (Exception e) { log.error("修改AD域用户属性失败",e); return false; } } /** * @Description:搜索指定节点下的所有AD域用户 * (经 测试此查询只返回了部分用户,可能是有返回条数的限制,使用时请注意,使用searchByUserName更精确) * @author moonxy * @date 2018-05-15 */ public void searchInformation(String searchBase) throws ServerException { try { SearchControls searchCtls = new SearchControls(); searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); String searchFilter = "objectClass=user"; String returnedAtts[] = { "memberOf" }; searchCtls.setReturningAttributes(returnedAtts); NamingEnumeration<SearchResult> answer = dc.search(searchBase, searchFilter, searchCtls); while (answer.hasMoreElements()) { SearchResult sr = answer.next(); log.info("<<<::[" + sr.getName() + "]::>>>>"); } } catch (Exception e) { log.error("查询指定节点下的AD用户异常",e); throw new ServerException("查询指定节点AD域用户异常"); } } /** * 启用用户 * @param userName */ public void enablePerson(String userName) throws ServerException { try { ModificationItem[] mods = new ModificationItem[1]; mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,new BasicAttribute("userAccountControl", "512")); dc.modifyAttributes("cn="+userName + "," + root, mods); log.info("启用用户成功!"); }catch(Exception e) { log.error("启用用户失败",e); throw new ServerException("启用AD用户失败"); } } /** * @Description:指定搜索节点搜索指定域用户 * @author moonxy * @date 2018-05-15 */ public SearchResult searchByUserName(String searchBase, String userName){ SearchControls searchCtls = new SearchControls(); searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); String searchFilter = "sAMAccountName=" + userName; //定制返回属性 String returnedAtts[] = { "sAMAccountName" }; //设置返回属性集 searchCtls.setReturningAttributes(returnedAtts); try { NamingEnumeration<SearchResult> answer = dc.search(searchBase, searchFilter, searchCtls); return answer.next(); } catch (Exception e) { log.error("指定搜索节点搜索指定域用户失败,未查询到用户",e); return null; } } /** * 修改指定用户密码 * @param name * @param newPassword */ public void changePassword(String name, String newPassword) throws ServerException { String userName = ("CN="+ name + ","+ root); //用户 try { ModificationItem[] mods = new ModificationItem[1]; String newQuotedPassword = "\"" + newPassword + "\""; byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE"); mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword)); // 修改密码 dc.modifyAttributes(userName, mods); log.info("Reset Password for: " + userName); dc.close(); } catch (Exception e) { log.error("Problem resetting password", e); if (e.toString().indexOf("LDAP: error code") > 0) { // 得到ldap返回的错误吗 String errorMsg = e.toString(); int startNum = errorMsg.toString().indexOf("LDAP: error code") + 17; errorMsg = errorMsg.substring(startNum, startNum + 19); int endNum = errorMsg.toString().indexOf(" - "); errorMsg = errorMsg.substring(0, endNum); if ("49".equals(errorMsg)) { errorMsg = "域控管理员账户/密码错误"; } else if ("32".equals(errorMsg) || "34".equals(errorMsg)) { errorMsg = "域内账户错误"; } else if ("10".equals(errorMsg)) { errorMsg = "dc错误"; } else { errorMsg = "修改失败,错误吗:" + errorMsg; } log.error("Problem resetting password(ldap):" + errorMsg); } else { if (e.toString().indexOf( "algorithm: Default, provider: SunJSSE, class: sun.security.ssl.SSLContextImpl$DefaultSSLContext") > 0) { log.error("Problem resetting password(ldap):" + "证书无效/证书密码错误"); } if (e.toString().indexOf( "the trustAnchors parameter must be non-empty") > 0) { log.error("Problem resetting password(ldap):" + "证书不存在"); } } throw new ServerException("修改指定用户密码失败"); } } }