JAVA使用Ldap操作AD域

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

说明:

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

(2). 连接ad域有两个地址: ldap://XXXXX.com:389ldap://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

public 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) {
 //要修改的帐号(这个dn是查询的用户信息里的dn的值,而不是域账号)
 String dn = json.getString("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质变之路

关注获取更多技能知识(看书太枯燥,可以先看视频,之后在看书会容易很多,吸收的更多)

1。 关注"Java质变之路"之后回复 “技能” 获取下列技能书籍


技能书籍.png

技能书籍.png

2。 关注"Java质变之路"之后回复 “扩展” 获取下列扩展书籍


扩展书籍.png

3。 关注"Java质变之路"之后回复 “视频” 获取更多视频


从初级到高手.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,271评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,725评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,252评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,634评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,549评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,985评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,471评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,128评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,257评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,233评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,235评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,940评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,528评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,623评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,858评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,245评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,790评论 2 339

推荐阅读更多精彩内容