JAVA使用Ldap操作AD域

Posted AJiSun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA使用Ldap操作AD域相关的知识,希望对你有一定的参考价值。

 

项目上遇到的需要在集成 操作域用户的信息的功能,第一次接触ad域,因为不了解而且网上其他介绍不明确,比较费时,这里记录下。

说明:

(1). 特别注意:Java操作查询域用户信息获取到的数据和域管理员在电脑上操作查询的数据可能会存在差异(同一个意思的表示字段,两者可能不同)。

(2). 连接ad域有两个地址: ldap://XXXXX.com:389 和 ldap://XXXXX.com:636(SSL)。

(3). 端口389用于一般的连接,例如登录,查询等非密码操作,端口636安全性较高,用户密码相关操作,例如修改密码等。

(4).  域控可能有多台服务器,之间数据同步不及时,可能会导致已经修改的数据被覆盖掉,这个要么域控缩短同步的时间差,要么同时修改每一台服务器的数据。

 

1. 389登录

// 只要不抛出异常就是验证通过
public
LdapContext adLogin(JSONObject json) { String username = json.getString("username"); String password = json.getString("password"); String server = "ldap://XXXXXXX.com:389"; try { Hashtable<String, String> env = new Hashtable<String, String>(); //用户名称,cn,ou,dc 分别:用户,组,域 env.put(Context.SECURITY_PRINCIPAL, username); //用户密码 cn 的密码 env.put(Context.SECURITY_CREDENTIALS, password); //url 格式:协议://ip:端口/组,域 ,直接连接到域或者组上面 env.put(Context.PROVIDER_URL, server); //LDAP 工厂 env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); //验证的类型 "none", "simple", "strong" env.put(Context.SECURITY_AUTHENTICATION, "simple"); LdapContext ldapContext = new InitialLdapContext(env, null); log.info("ldapContext:" + ldapContext); log.info("用户" + username + "登录验证成功"); return ldapContext; } catch (NamingException e) { log.info("用户" + username + "登录验证失败"); log.info("错误信息:"+e.getExplanation()); return null; } }

 

2. 636登录验证(需要导入证书)

//证书提前倒入的Java库中
// 参考:https://www.cnblogs.com/moonson/p/4454159.html

LdapContext adLoginSSL(JSONObject json) {
String username = json.getString("username");
String password = json.getString("password");
Hashtable env = new Hashtable();

String javaHome = System.getProperty("java.home");
         String keystore = javaHome+"/lib/security/cacerts";
         log.info("java.home,{}",keystore);
    // 加载导入jdk的域证书
         System.setProperty("javax.net.ssl.trustStore", keystore);
         System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
         String LDAP_URL = "ldap://XXXXXX.com:636"; // LDAP访问地址

         env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
         env.put(Context.SECURITY_PROTOCOL, "ssl");//链接认证服务器
         env.put(Context.PROVIDER_URL, LDAP_URL);
         env.put(Context.SECURITY_AUTHENTICATION, "simple");
         env.put(Context.SECURITY_PRINCIPAL, username);
         env.put(Context.SECURITY_CREDENTIALS, password);
         try {
             LdapContext ldapContext = new InitialLdapContext(env, null);
             log.info("认证成功");// 这里可以改成异常抛出。
             return ldapContext;
         } catch (javax.naming.AuthenticationException e) {
             log.info("认证失败:{}",e.getMessage());
         } catch (Exception e) {
             log.info("认证出错:{}",e.getMessage());
         }
        return null;
    }

 

 3. 查询域用户信息

public List getUserKey(JSONObject json){

        JSONObject admin = new JSONObject();
        admin.put("username","Aaaaa");
        admin.put("password", "bbbbbbbb");
        String name = json.getString("name");
        log.info("需要查询的ad信息:{}",name);
        List<JSONObject> resultList = new JSONArray();
        LdapContext ldapContext = adLogin(admin); //连接到域控
        if (ldapContext!=null){

            String company = "";
            String result = "";
            try {
                // 域节点
                String searchBase = "DC=XXXXXXX,DC=com";
                // LDAP搜索过滤器类
                //cn=*name*模糊查询 
         //cn=name 精确查询           
// String searchFilter = "(objectClass="+type+")"; String searchFilter = "(sAMAccountName="+name+")"; //查询域帐号 // 创建搜索控制器 SearchControls searchCtls = new SearchControls(); String returnedAtts[]={"description","sAMAccountName","userAccountControl"};
searchCtls.setReturningAttributes(returnedAtts); //设置指定返回的字段,不设置则返回全部 // 设置搜索范围 深度 searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE); // 根据设置的域节点、过滤器类和搜索控制器搜索LDAP得到结果 NamingEnumeration answer = ldapContext.search(searchBase, searchFilter,searchCtls); // 初始化搜索结果数为0 int totalResults = 0; int rows = 0; while (answer.hasMoreElements()) {// 遍历结果集 SearchResult sr = (SearchResult) answer.next();// 得到符合搜索条件的DN ++rows; String dn = sr.getName(); log.info(dn); Attributes Attrs = sr.getAttributes();// 得到符合条件的属性集 if (Attrs != null) { try { for (NamingEnumeration ne = Attrs.getAll(); ne.hasMore();) { Attribute Attr = (Attribute) ne.next();// 得到下一个属性 // 读取属性值 for (NamingEnumeration e = Attr.getAll(); e.hasMore(); totalResults++) { company = e.next().toString(); JSONObject tempJson = new JSONObject(); tempJson.put(Attr.getID(), company.toString()); resultList.add(tempJson); } } } catch (NamingException e) { log.info("Throw Exception : " + e.getMessage()); } } } log.info("总共用户数:" + rows); } catch (NamingException e) { log.info("Throw Exception : " + e.getMessage()); }finally { try{ ldapContext.close(); }catch (Exception e){ e.printStackTrace(); } } } return resultList; }

 

 4. 重置用户密码

 

// 管理员重置用户密码,后强制用户首次登录修改密码
public Map<String, String> updateAdPwd(JSONObject json) {
        String dn = json.getString("dn");//要修改的帐号(这个dn是查询的用户信息里的dn的值,而不是域账号
        String password = json.getString("password");//新密码

        JSONObject admin = new JSONObject();
        admin.put("username","aaaaaaa");
        admin.put("password", "bbbbbbb");
        Map<String,String> map = new HashMap<String,String>();
        LdapContext ldapContext = adLoginSSL(admin); //连接636端口域
        ModificationItem[] mods = new ModificationItem[2];
        if (ldapContext!=null){
            try {
                String newQuotedPassword = "\\"" + password + "\\"";
                byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
// unicodePwd:修改的字段,newUnicodePassword:修改的值
                mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
                        new BasicAttribute("unicodePwd", newUnicodePassword));
mods[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
                        new BasicAttribute("pwdLastSet", "0"));  // 首次登录必须修改密码


                // 修改密码
                ldapContext.modifyAttributes(dn, mods); 
                map.put("result", "S");
                map.put("message","成功");
            }catch (Exception e){ 
                map.put("result","E");
                map.put("message", "无法重置密码");
            }finally {
                try{
                    ldapContext.close();
                }catch (Exception e){
                    e.printStackTrace();
                }

            }

        }else {
            log.info("");
            map.put("result","E");
            map.put("message", "验证失败");
        }


        return map;
    }

 

 5. 域账号解锁

 

// 表示锁定的字段需要测试,不一定这个lockoutTime
public
Map<String, String> deblocking(JSONObject json) { JSONObject admin = new JSONObject(); String dn = json.getString("dn"); //被解锁的帐号(这个dn指的是查询用户信息里的dn的值,不是域账号 admin.put("username","aaaaaa"); admin.put("password","bbbbbb"); Map<String,String> map = new HashMap<String,String>(); LdapContext ldapContext = adLogin(admin); ModificationItem[] mods = new ModificationItem[1]; if (ldapContext!=null){ try {       // "0" 表示未锁定,不为0表示锁定 mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("lockoutTime","0")); // 解锁域帐号 ldapContext.modifyAttributes(dn, mods); map.put("result", "S"); map.put("message","成功"); }catch (Exception e){ map.put("result","E"); map.put("message", "解锁失败"); }finally { try{ ldapContext.close(); }catch (Exception e){ e.printStackTrace(); } } }else { map.put("result","E"); map.put("message", "验证失败"); } return map; }

 

以上是关于JAVA使用Ldap操作AD域的主要内容,如果未能解决你的问题,请参考以下文章

AD 域服务简介- Java 对 AD 域用户的增删改查操作

关于java通过LDAP实现AD域添加用户的问题

AD 域服务简介- Java 获取 AD 域用户

操作AD的用户信息(通过LDAP应用)

java 同步用户信息到AD域

Spring Boot Security ldap auth针对多个独立的AD域