java查询AD系统用户、新增、删除用户

包含了我查询、删除遇到的坑
1.只能查询1000条的结果。
2.无法删除AD用户下存在手机信息问题。

直接上代码

package com.example.demo.test.adTest;

import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.*;
import java.util.*;

public class ADUserUtils {
    LdapContext dc = null;
    String root = "DC=ceshi,DC=com"; // LDAP的根节点的DC
    String addRoot = "OU=测试部门,OU=XXX公司,DC=ceshi,DC=com"; // 新增用户的节点
	String delRoot = "CN=王五,OU=测试部门,OU=XXX公司,DC=ceshi,DC=com"; // LDAP的根节点的DC
	
    public static void main(String[] args) {
        String userUrl = "";
        ADUserUtils utils = new ADUserUtils();
        String newUserName = "王五";
        String newUserNameShort = "wangw";
        //查询用户
        Map str = utils.searchByUserName(utils.root, newUserNameShort);
        //新增用户
        utils.add(newUserName,newUserNameShort);
        //删除AD用户
        utils.delete(utils.delRoot);
        utils.close();
    }

    public ADUserUtils() {
        super();
        init();
    }

    /**
     * @Description:初始化AD域服务连接
     * @author maxb
     * @date 2022-03-25
     */
    public void init() {
        Properties env = new Properties();
        String adminName = "userName";//AD管理员系统的账号一般:userName@ceshi.com 或者 userName
        String adminPassword = "password";//password
        String ldapURL = "ldap://" + host + ":" + post;//ip:192.168.0.0.1      port:端口默认389
        env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");// LDAP工厂类
        env.put(Context.SECURITY_AUTHENTICATION, "simple");//LDAP访问安全级别:"none","simple","strong"
        env.put(Context.SECURITY_PRINCIPAL, adminName);
        env.put(Context.SECURITY_CREDENTIALS, adminPassword);
        env.put(Context.PROVIDER_URL, ldapURL);
        try {
            dc = new InitialLdapContext(env, null);
            System.out.println("AD域服务连接认证成功");
        } catch (Exception e) {
            System.out.println("AD域服务连接认证失败");
            e.printStackTrace();
        }
    }

    /**
     * @Description:关闭AD域服务连接
     * @author maxb
     * @date 2022-03-25
     */
    public void close() {
        if (dc != null) {
            try {
                dc.close();
            } catch (NamingException e) {
                System.out.println("NamingException in close():" + e);
            }
        }
    }

    /**
     * @Description:新增AD域用户
     * @author maxb
     * @date 2022-03-25
     */
    public void add(String newUserName,String newUserNameShort) {
        try {
            Attributes attrs = new BasicAttributes(true);
            attrs.put("objectClass", "user");
            attrs.put("samAccountName", newUserNameShort);
            attrs.put("displayName", newUserName);
            attrs.put("userPrincipalName", newUserNameShort + "@chinatelling.com");
            dc.createSubcontext("CN=" + newUserName + "," + addRoot, attrs);
            System.out.println("新增AD域用户成功:" + newUserName);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("新增AD域用户失败:" + newUserName);
        }
    }
    
	/**
     * @Description:删除AD域用户
     * @author maxb
     * @date 2022-03-25
     */
    public void delete(String dn) {
        try {
            dc.destroySubcontext(dn);
            System.out.println("删除AD域用户成功:" + dn);
        } catch (Exception e) {
            System.out.println("删除AD域用户失败:" + dn);
            e.printStackTrace();
        }
    }


    /**
     * @Description:指定搜索节点搜索指定域用户
     * @author maxb
     * @date 2022-03-25
     */
    public Map searchByUserName(String searchBase, String userName) {
        Map resultMap = new HashMap();
        List<Map<String,String>> userList = new ArrayList<Map<String,String>>();
        SearchControls searchCtls = new SearchControls();
        searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        String searchFilter =  "(&(objectClass=user))"; 
        String returnedAtts[] = { "memberOf", "distinguishedName","Pwd-Last-Set", "User-Password", "cn"}; //定制返回属性
        searchCtls.setReturningAttributes(returnedAtts); //设置返回属性集
        try {
            NamingEnumeration<SearchResult> answer = dc.search(searchBase,searchFilter, searchCtls);
            while (answer.hasMoreElements()) {// 遍历结果集
                SearchResult sr = answer.next();
                Attributes Attrs = sr.getAttributes();// 得到域用户属性集
                if (Attrs != null) {
                    try {
                        Map<String,String> m = new HashMap();
                        for (NamingEnumeration ne = Attrs.getAll(); ne.hasMore();) {
                            Attribute Attr = (Attribute) ne.next();// 得到下一个属性
                            String key = Attr.getID().toString();
                            // 读取属性值
                            for (NamingEnumeration e = Attr.getAll(); e.hasMore();) {
                                String value= e.next().toString();
                                m.put(key, value);
                            }
                        }
                        userList.add(m);
                    } catch (Exception e) {
                        System.err.println("Throw Exception : " + e);
                    }
                }
            }//while
            //获取指定用户信息
            for (int i = 0; i < userList.size(); i++) {
                Map<String, String> m = userList.get(i);
                for(Map.Entry<String, String> entry : m.entrySet()){
                    String mapKey = entry.getKey();
                    String mapValue = entry.getValue();
                    System.out.println("第"+(i+1)+"个用户key = "+mapKey+" , value = "+mapValue);
                }
            }
        } catch (Exception e) {
            System.err.println("指定搜索节点搜索指定域用户失败");
            e.printStackTrace();
        }
        return resultMap;
    }
}

查询过程出现的坑:查询结果只展示1000条数据

  • 方案一:直接扩大AD域的MaxPageSize(不推荐),具体可百度查询其他博主的,网上好多
  • 方案二:可分页查询(我参考的一位大佬的 分页查询AD结果)
/**
     * @Description:指定搜索节点搜索指定域用户(分页)
     * @author maxb
     * @date 2022-03-26
     */
    public Map searchByUserName(String searchBase, String userName) {
        Map resultMap = new HashMap();
        List<Map<String,String>> userList = new ArrayList<Map<String,String>>();
        SearchControls searchCtls = new SearchControls();
        searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        int pageSize = 1000;//程序单次查询最大条数
        String searchFilter =  "(&(objectClass=user))";
        String returnedAtts[] = { "memberOf", "distinguishedName","Pwd-Last-Set", "User-Password", "cn"}; //定制返回属性
        searchCtls.setReturningAttributes(returnedAtts); //设置返回属性集
        try {
            byte[] cookie = null;//用于判断是否还有剩余数据(进行分页)
            dc.setRequestControls(new Control[]{new PagedResultsControl(pageSize, Control.CRITICAL)});
            do{
                NamingEnumeration<SearchResult> answer = dc.search(searchBase,searchFilter, searchCtls);
                Object o = answer.nextElement();
                while (answer.hasMoreElements()) {// 遍历结果集
                    SearchResult sr = answer.next();
                    Attributes Attrs = sr.getAttributes();// 得到域用户属性集
                    if (Attrs != null) {
                        try {
                            Map<String,String> m = new HashMap();
                            for (NamingEnumeration ne = Attrs.getAll(); ne.hasMore();) {
                                Attribute Attr = (Attribute) ne.next();// 得到下一个属性
                                String key = Attr.getID().toString();
                                // 读取属性值
                                for (NamingEnumeration e = Attr.getAll(); e.hasMore();) {
                                    String value= e.next().toString();
                                    m.put(key, value);

                                }
                            }
                            userList.add(m);
                        } catch (Exception e) {
                            System.err.println("Throw Exception : " + e);
                        }
                    }
                }//while
                Control[] controls = dc.getResponseControls();
                if (controls != null) {
                    for (int i = 0; i < controls.length; i++) {
                        if (controls[i] instanceof PagedResultsResponseControl) {
                            PagedResultsResponseControl prrc = (PagedResultsResponseControl) controls[i];
                            cookie = prrc.getCookie();
                        }
                    }
                }
                // 将cookie提供给LdapContext,让它在接下来的查询中进行换页
                dc.setRequestControls(new Control[]{new PagedResultsControl(pageSize, cookie, Control.CRITICAL)});
            }while (cookie != null);
            //获取指定用户信息
            for (int i = 0; i < userList.size(); i++) {
                Map<String, String> m = userList.get(i);
                for(Map.Entry<String, String> entry : m.entrySet()){
                    String mapKey = entry.getKey();
                    String mapValue = entry.getValue();
                    System.out.println("第"+(i+1)+"个用户key = "+mapKey+" , value = "+mapValue);
                }
            }
        } catch (Exception e) {
            System.err.println("指定搜索节点搜索指定域用户失败");
            e.printStackTrace();
        }finally {
            if (null != dc) {
                try {
                    dc.close();
                    dc = null;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return resultMap;
    }

java 删除AD用户出现的坑:

这个坑是因为我们系统里的AD用户用手机登录过邮箱,所以在AD账户下级自动创建了手机信息,导致直接执行删除时,无法删除。报6003错误
解决方案:递归删除

String node= "CN=王五,OU=测试部门,OU=XXX公司,DC=ceshi,DC=com";

public void delTree(String node){
         NamingEnumeration<?> ne = null;
         try {
             ne = dc.listBindings(node);//枚举绑定在指定上下文中的名称,以及绑定到这些名称的对象
             while(ne.hasMore()){
                    Binding bing = (Binding)ne.next();
                    String newNode = bing.getName() + "," + node;
                    System.out.println(node + "的子节点" + newNode);
                    delTree(newNode);//递归
                }
            } catch (NamingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            delete(node);//删除无子节点的节点
    }