JAVA使用Ldap操作AD域的方法示例

 更新时间:2019年04月17日 14:41:43   转载 作者:AJiSun  
这篇文章主要介绍了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的增删改查询

package com.smnpc.util;
 
import java.util.Hashtable;
import java.util.Vector;
 
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapContext;
 
/**
* Java通过Ldap操作AD的增删该查询
* @author guob
*/
 
public class LdapbyUser {
DirContext dc = null;
String root = "dc=example,dc=com"; // LDAP的根节点的DC
 
/**
* 
* @param dn类似于"CN=RyanHanson,dc=example,dc=com"
* @param employeeID是Ad的一个员工号属性
*/
public LdapbyUser(String dn,String employeeID) {
init();
// add();//添加节点
// delete("ou=hi,dc=example,dc=com");//删除"ou=hi,dc=example,dc=com"节点
// renameEntry("ou=new,o=neworganization,dc=example,dc=com","ou=neworganizationalUnit,o=neworganization,dc=example,dc=com");//重命名节点"ou=new,o=neworganization,dc=example,dc=com"
// searchInformation("dc=example,dc=com", "", "sAMAccountName=guob");//遍历所有根节点
modifyInformation(dn,employeeID);//修改
// Ldapbyuserinfo("guob");//遍历指定节点的分节点
close();
}
 
/**
* 
* Ldap连接
* 
* @return LdapContext
*/
public void init() {
Hashtable env = new Hashtable();
String LDAP_URL = "ldap://xxxx:389"; // LDAP访问地址
String adminName = "example\\user"; // 注意用户名的写法:domain\User或
String adminPassword = "userpassword"; // 密码
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, LDAP_URL);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, adminName);
env.put(Context.SECURITY_CREDENTIALS, adminPassword);
try {
dc = new InitialDirContext(env);// 初始化上下文
System.out.println("认证成功");// 这里可以改成异常抛出。
} catch (javax.naming.AuthenticationException e) {
System.out.println("认证失败");
} catch (Exception e) {
System.out.println("认证出错:" + e);
}
}
 
/**
* 添加
*/
public void add(String newUserName) {
try {
BasicAttributes attrs = new BasicAttributes();
BasicAttribute objclassSet = new BasicAttribute("objectClass");
objclassSet.add("sAMAccountName");
objclassSet.add("employeeID");
attrs.put(objclassSet);
attrs.put("ou", newUserName);
dc.createSubcontext("ou=" + newUserName + "," + root, attrs);
} catch (Exception e) {
e.printStackTrace();
System.out.println("Exception in add():" + e);
}
}
 
/**
* 删除
* 
* @param dn
*/
public void delete(String dn) {
try {
dc.destroySubcontext(dn);
} catch (Exception e) {
e.printStackTrace();
System.out.println("Exception in delete():" + e);
}
}
 
/**
* 重命名节点
* 
* @param oldDN
* @param newDN
* @return
*/
public boolean renameEntry(String oldDN, String newDN) {
try {
dc.rename(oldDN, newDN);
return true;
} catch (NamingException ne) {
System.err.println("Error: " + ne.getMessage());
return false;
}
}
 
/**
* 修改
* 
* @return
*/
public boolean modifyInformation(String dn,String employeeID) {
try {
System.out.println("updating...\n");
ModificationItem[] mods = new ModificationItem[1];
/* 修改属性 */
// Attribute attr0 = new BasicAttribute("employeeID", "W20110972");
// mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr0);
 
/* 删除属性 */
// Attribute attr0 = new BasicAttribute("description",
// "陈轶");
// mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE,
// attr0);
 
/* 添加属性 */
Attribute attr0 = new BasicAttribute("employeeID",employeeID);
mods[0] = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr0);
/* 修改属性 */
dc.modifyAttributes(dn+",dc=example,dc=com", mods);
return true;
} catch (NamingException e) {
e.printStackTrace();
System.err.println("Error: " + e.getMessage());
return false;
}
}
 
/**
* 关闭Ldap连接
*/
public void close() {
if (dc != null) {
try {
dc.close();
} catch (NamingException e) {
System.out.println("NamingException in close():" + e);
}
}
}
 
/**
* @param base :根节点(在这里是"dc=example,dc=com")
* @param scope :搜索范围,分为"base"(本节点),"one"(单层),""(遍历)
* @param filter :指定子节点(格式为"(objectclass=*)",*是指全部,你也可以指定某一特定类型的树节点)
*/
public void searchInformation(String base, String scope, String filter) {
SearchControls sc = new SearchControls();
if (scope.equals("base")) {
sc.setSearchScope(SearchControls.OBJECT_SCOPE);
} else if (scope.equals("one")) {
sc.setSearchScope(SearchControls.ONELEVEL_SCOPE);
} else {
sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
}
NamingEnumeration ne = null;
try {
ne = dc.search(base, filter, sc);
// Use the NamingEnumeration object to cycle through
// the result set.
while (ne.hasMore()) {
System.out.println();
SearchResult sr = (SearchResult) ne.next();
String name = sr.getName();
if (base != null && !base.equals("")) {
System.out.println("entry: " + name + "," + base);
} else {
System.out.println("entry: " + name);
}
 
Attributes at = sr.getAttributes();
NamingEnumeration ane = at.getAll();
while (ane.hasMore()) {
Attribute attr = (Attribute) ane.next();
String attrType = attr.getID();
NamingEnumeration values = attr.getAll();
Vector vals = new Vector();
// Another NamingEnumeration object, this time
// to iterate through attribute values.
while (values.hasMore()) {
Object oneVal = values.nextElement();
if (oneVal instanceof String) {
System.out.println(attrType + ": " + (String) oneVal);
} else {
System.out.println(attrType + ": " + new String((byte[]) oneVal));
}
}
}
}
} catch (Exception nex) {
System.err.println("Error: " + nex.getMessage());
nex.printStackTrace();
}
}
/**
* 查询
* 
* @throws NamingException
*/
public void Ldapbyuserinfo(String userName) {
// Create the search controls
SearchControls searchCtls = new SearchControls();
// Specify the search scope
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
// specify the LDAP search filter
String searchFilter = "sAMAccountName=" + userName;
// Specify the Base for the search 搜索域节点
String searchBase = "DC=example,DC=COM";
int totalResults = 0;
String returnedAtts[] = { "url", "whenChanged", "employeeID", "name",
"userPrincipalName", "physicalDeliveryOfficeName",
"departmentNumber", "telephoneNumber", "homePhone", "mobile",
"department", "sAMAccountName", "whenChanged", "mail" }; // 定制返回属性
 
searchCtls.setReturningAttributes(returnedAtts); // 设置返回属性集
 
// searchCtls.setReturningAttributes(null); // 不定制属性,将返回所有的属性集
 
try {
NamingEnumeration answer = dc.search(searchBase, searchFilter,
searchCtls);
if (answer == null || answer.equals(null)) {
System.out.println("answer is null");
} else {
System.out.println("answer not null");
}
while (answer.hasMoreElements()) {
SearchResult sr = (SearchResult) answer.next();
System.out
.println("************************************************");
System.out.println("getname=" + sr.getName());
Attributes Attrs = sr.getAttributes();
if (Attrs != null) {
try {
 
for (NamingEnumeration ne = Attrs.getAll(); ne
.hasMore();) {
Attribute Attr = (Attribute) ne.next();
System.out.println("AttributeID="
+ Attr.getID().toString());
// 读取属性值
for (NamingEnumeration e = Attr.getAll(); e
.hasMore(); totalResults++) {
String user = e.next().toString(); // 接受循环遍历读取的userPrincipalName用户属性
System.out.println(user);
}
// System.out.println(" ---------------");
// // 读取属性值
// Enumeration values = Attr.getAll();
// if (values != null) { // 迭代
// while (values.hasMoreElements()) {
// System.out.println(" 2AttributeValues="
// + values.nextElement());
// }
// }
// System.out.println(" ---------------");
}
} catch (NamingException e) {
System.err.println("Throw Exception : " + e);
}
}
}
System.out.println("Number: " + totalResults);
} catch (Exception e) {
e.printStackTrace();
System.err.println("Throw Exception : " + e);
}
}
 
/**
* 主函数用于测试
* @param args
*/
public static void main(String[] args) {
new LdapbyUser("CN=RyanHanson","bbs.it-home.org");
}
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Spring Boot插件spring tool suite安装及使用详解

    Spring Boot插件spring tool suite安装及使用详解

    这篇文章主要介绍了Spring Boot插件spring tool suite安装及使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • Java简单实现约瑟夫环算法示例

    Java简单实现约瑟夫环算法示例

    这篇文章主要介绍了Java简单实现约瑟夫环算法,简单描述了约瑟夫环问题,并结合实例形式分析了Java实现约瑟夫环的具体操作技巧,需要的朋友可以参考下
    2017-09-09
  • SpringBoot2.0整合WebSocket代码实例

    SpringBoot2.0整合WebSocket代码实例

    这篇文章主要介绍了SpringBoot2.0整合WebSocket代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • Java从控制台接受输入字符的简单方法

    Java从控制台接受输入字符的简单方法

    这篇文章主要介绍了Java从控制台接受输入字符的简单方法,需要的朋友可以参考下
    2014-02-02
  • Java 中ThreadLocal类详解

    Java 中ThreadLocal类详解

    什么是ThreadLocal?顾名思义它是local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。
    2016-03-03
  • Java实现微信公众号获取临时二维码功能示例

    Java实现微信公众号获取临时二维码功能示例

    这篇文章主要介绍了Java实现微信公众号获取临时二维码功能,结合实例形式分析了java调用微信公众号接口实现临时二维码生成功能相关操作技巧,需要的朋友可以参考下
    2019-10-10
  • spring mvc实现文件上传并携带其他参数的示例

    spring mvc实现文件上传并携带其他参数的示例

    本篇文章主要介绍了spring mvc实现文件上传并携带其他参数的示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • 详解springboot整合Listener的两种方式

    详解springboot整合Listener的两种方式

    这篇文章主要介绍了springboot整合Listener的两种方式,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-12-12
  • Java性能优化技巧汇总

    Java性能优化技巧汇总

    这篇文章主要介绍了Java性能优化技巧,汇总了提升java程序性能的常用技巧,非常具有实用价值,需要的朋友可以参考下
    2015-04-04
  • 基于maven使用IDEA创建多模块项目

    基于maven使用IDEA创建多模块项目

    这篇文章主要介绍了基于maven使用IDEA创建多模块项目,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04

最新评论