使用JAVA控制AD域进行基本的操作详解

前段时间,我接到了一个新任务:使用JAVA控制AD域进行基本的操作。经几天摸索也看来多位博友的代码,现在摸索出了一套较为详细的Java操控AD域的方法。废话不多说,现进入正题》》》

一、安装域控服务器

什么是域控服务器我就不多说了,请各位自行百度,安装的教程在下面的连接里:http://www.jb51.net/os/windows/win2008/69883.html

二、安装证书控制服务

  1. AD域控安装证书服务

    https://wenku.baidu.com/view/91fad77b5acfa1c7aa00ccbc.html

  2. ad域上拿ca证书文件

    http://ad域服务器IP/certsrv/

  1)点链接:下载 CA证书、证书链或CRL

  2)选择编码base64

  3)点链接:下载 CA证书

  ps:这里采用的是结合ad服务器上的证书颁发机构,如果要采用第三方机构发布的证书需要另行配置,详情看我的另一篇文章。

 3. 根据CA证书生成keystore文件

三、ping通虚拟机

我相信大部分人做这个开发应该是在虚拟机上做开发测试的,ping通虚拟机是必不可少的。

四、编写Java代码

如果你完成了上述步骤,那么恭喜你,基本的环境你已经搭建完成了,现在可以开始我们的开发。首先介绍一下AD域的用户存储,实际上就是一个ldap的目录存储过程,所以我们可以根据对ldap的访问模式访问到ad域上的对象,例如我想访问AD与上名为上海的OU下的一个用户test1,那么我可以访问“CN=test1,OU=上海,DC=adserv,DC=com”(后面的DC是我的域控服务器的名字如:adserv.com)。知道了如何访问指定对象,那么接下来的事情不是变得简单了?下面是我借鉴了网上的一些大牛的代码,自己编写的操作类如有什么异常,非常欢迎您的指点哦

1.第一步 创建Ldap连接,因为AD域的连接控制本质上就是对Ldap的控制,所以获取了ldap的连接就是获取了AD域的操作权。

 
  

2.第二步 完成上述代码之后下面的增删查改,只要通过getConnect()方法获取链接就可以进行操作。 

a.添加用户操作新建用户的话一般新建在一个OU(组织结构)中,而我的需求中是要对在添加用户的时候输入用户名和所属OU,用户不存在则创建,若该用户存在就移入新的OU,如果OU不存就逐层创建OU再保存这个用户。用户信息的话是使用一个Attributes对象进行包装由前台传进来的(这里需要说明的是,每一个对象都有自己特定的类型,所以们在创建对象时必须要确定创建的类型,就是下文的type,我将他提取出来),具体的对象属性可以参考这篇文章《AD域用户属性》还有对新的用户账号的权限设置《AD户属性userAccountControl的详细解释》,下面是实现代码:

	public static boolean addUser(String userOU, String userName,String passWord, Attributes attrs, String type)throws NamingException {
		final int UF_PASSWD_NOTREQD = 0x0020;
		final int UF_NORMAL_ACCOUNT = 0x0200;
		final int UF_PASSWORD_EXPIRED = 0x800000;
		LdapContext ctx = null;
		String userDN = "";
		try {		
			if (!"organizationalUnit".equals(type)) {
				userDN = "CN=" + userName + ",";
			} else {
				userDN = "OU=" + userName + ",";
			}
			ctx = getConn();
			// create the organizational structure that does not exist
			String[] OU = userOU.split(",");
			String ou = "";
			for (int i = OU.length - 1; i >= 0; i--) {
				ou = "OU=" + OU[i] + "," + ou;
				if (i == 0)
					userDN = userDN + ou + "DC=adserv,DC=com";
				String OUDN = ou + "DC=adserv,DC=com";
				try {
					ctx.getAttributes(OUDN);
				} catch (NameNotFoundException e) {
					addOU(OUDN);
				}
			}
			try {
			if ("organizationalUnit".equals(type)) {
				attrs.put("objectClass", "organizationalUnit");
				ctx.createSubcontext(userDN, attrs);
				return true;
			}
			// required attributes
			attrs.put("objectClass", type);
			attrs.put("sAMAccountName", userName);
				ctx.createSubcontext(userDN, attrs);
			} catch (NameAlreadyBoundException e) {
				if("organizationalUnit".equals(type)){
					System.out.println("The organizational unit already exists");
					return false;
				}else{
				String oldDN = searchEntity(userName, type);
				changeOU(oldDN, ou);
				modifyUser(attrs, userName, type);
				System.out.println("User already exists, has been moved into the organization unit");
				}
			}
			if(passWord!=null){
			ModificationItem[] mods = new ModificationItem[2];
			// format the password
			String newQuotedPassword = "\"" + passWord + "\"";
			//format password
			byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
			mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
					new BasicAttribute("unicodePwd", newUnicodePassword));
			mods[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, 
					new BasicAttribute("userAccountControl",
							Integer.toString(UF_NORMAL_ACCOUNT
									+ UF_PASSWORD_EXPIRED + UF_PASSWD_NOTREQD)));
			// save the password
			ctx.modifyAttributes(userDN, mods);
			mods = null;}
			return true;
		} catch (NamingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (ctx != null) {
				ctx.close();
				ctx = null;
			}
		}
		return false;
	}

ps:如果使用的389接口的话使用上述代码还是能实现创建对象,但是创建时会报错(据我估计就是安全错误,要求你使用ssl),并且创建的对象时无法设置密码的,但是如果你们可以不使用ssl设置密码的话请教教我,谢谢。、
b.用户删除
删除操作较为简单,确定删的对象类型(对象类型会在下面内容中介绍)就可以了。代码如下:


  	public static boolean delEntity(String entityName,String entityType) throws NamingException  {
    		LdapContext ctx = null;
    			try{
 	   			String DN=searchEntity(entityName,entityType);
    				ctx=getConn();
    				if(DN!=null){
    					ctx.destroySubcontext(DN);
  	  			}else{
    					System.out.println("delete Entity not exist");
    					return false;
    				}
    	        return true;
    	    } catch (Exception e) {
        	    System.err.println("Problem: " + e);
     	   } finally {
        	    if (ctx != null) {
                	    ctx.close();
                	ctx = null;
           	 }
       	 	}
       		 return false;
    	}	
 
c.查找对象
查找兑现,与上次对象一样,要确定对象的类型,并且要确定对象名称。
我收集了几个对象的类型表达:
用户:(&(objectCategory=person)(objectClass=user);
策划机:computer;组:group; 
接洽人:contact ;共享文件夹:volume;
打印机:printQueue
下面是查找的实现代码:


	public static String searchEntity(String entityName, String entityType)
			throws NamingException {
		LdapContext ctx = null;
		String OUName = "";
		try {
			ctx = getConn();
			SearchControls searchCtls = new SearchControls();
			searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
			String searchFilter = null;
			if ("user".equals(entityType)) {
				searchFilter = "(&(objectCategory=person)(objectClass=user)(cn="
						+ entityName + "))";
			} else {
				String[] OUList = entityName.split(",");
				entityName = "";
				for (int i = OUList.length - 1; i >= 0; i--) {
					entityName = "OU=" + OUList[i] + "," + entityName;
					OUName = OUList[0];
				}
				searchFilter = "(&(objectCategory=organizationalUnit)(objectclass=organizationalUnit)(OU="
						+ OUName + "))";
			}
			String searchBase = "DC=adserv,DC=com";
			String returnedAtts[] = { "memberOf" };
			searchCtls.setReturningAttributes(returnedAtts);
			NamingEnumeration answer = ctx.search(searchBase,
					searchFilter, searchCtls);
			SearchResult sr = null;
			while (answer.hasMoreElements()) {
				sr = (SearchResult) answer.next();
				if (!"".equals(OUName))
					return entityName + "DC=adserv,DC=com";
				return sr.getName() + ",DC=adserv,DC=com";
			}
		} catch (NullPointerException e) {
			System.out.println("Entity not exist");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (ctx != null) {
				ctx.close();
				ctx = null;
			}
		}
		return null;
	}

c.修改对象
修改对象的属性可以同个对对象的数据进行修改,封装传入,程序会比较两个对象比较修改过得地方修改保存,为修改的就不修改代码如下:

 	public static boolean modifyUser(Attributes attrs, String entityName,String entityType) throws NamingException {
        	LdapContext ctx = null;
        	try {
        		String userDN=searchEntity(entityName,entityType);
            	ctx=getConn();
            	ctx.modifyAttributes(userDN, DirContext.REPLACE_ATTRIBUTE, attrs);
            	return true;
        	} catch (Exception e) {
            	System.err.println("Problem: " + e);
        	} finally {
            	if (ctx != null) {
                	try {
                    	ctx.close();
                	} catch (NamingException e) {
                    		e.printStackTrace();
                	}
                	ctx = null;
            	}
        	}
        	return false;
    	}

修改对象的组织结构则是对对象名字进行修改:
   	 public static boolean changeOU(String olduserDN, String newOU) throws NamingException  {
    		LdapContext ctx =null;
    		try{
    			ctx=getConn();
        		String[] user=olduserDN.split(",");
        		String newDN=user[0]+","+newOU+user[user.length-2]+","+user[user.length-1];
            	ctx.rename(olduserDN,newDN);
           	 return true;
            	}catch(Exception e){
    				e.printStackTrace();
    			} finally {
            	if (ctx != null) {
                    	ctx.close();
                	ctx = null;
            	}
            }
    	    return false;
    	}


好了,今天暂时给大家介绍到这里,之后我会简单的介绍一下组等其他对象的操作。目前,我能做已经能做到对AD域上的大部分对象的处理,如组,打印机,联系人,上面这一段代码,复制进入就能用了,有什么问题需要讨论可以随时发私信给我。

-----------------------------2017/12/5----------------------------------------

有读者说有一部分代码没有发出来,拉下了不好意思,现在补上

 addOU:添加组织机构方法
public static boolean addOU(String OUDN) throws NamingException {
LdapContext ctx = null;
try {
ctx = getConn();
// create a new OU
Attributes attrs = new BasicAttributes(true);
attrs.put("objectClass", "organizationalUnit");
ctx.createSubcontext(OUDN, attrs);
return true;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ctx != null) {
ctx.close();
ctx = null;
}
}
return false;
}

getConn:获取ad域连接;
getConnectionFromFool:获取ad域连接池

/**
 * 获取连接通道,如果是636接口就返回加密通道,否则返回普通通道
 * @param keyStore keyStore的文件目录
 * @param sslPassWord keyStore的密码
 * @param adPassWord ad域密码
 * @param ldapUrl ad域地址
 * @param port 使用的接口类型636/389
 * @return
 * @throws NamingException
 */
public static LdapContext getConnectionFromFool(String keyStore, String sslPassWord, String adPassWord,
String ldapUrl, String port) throws NamingException {
Properties env = new Properties();
if ("636".equals(port)) {
System.setProperty("javax.net.ssl.trustStore", keyStore);
System.setProperty("javax.net.ssl.trustStorePassword", sslPassWord);
env.put(Context.SECURITY_PROTOCOL, "ssl");
}
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://" + ldapUrl);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=Administrator,cn=Users,dc=adserv,dc=com");//第一个cn为你的ad域管理员账户名
env.put(Context.SECURITY_CREDENTIALS, adPassWord);
env.put("com.sun.jndi.ldap.connect.pool", "true");
env.put("java.naming.referral", "follow");
return new InitialLdapContext(env, null);
}
public static LdapContext getConn() throws NamingException {
//这边就是读取配置文件中的接口
//	String port = null;
//	if (DomainPushConfig.getInstance().useSsl) {
//	port = SSL_LDAP_URL_Port;
//	} else {
//	port = WITHOUT_SSL_LDAP_URL_Port;
//	}   下面为389连接
	return getConnectionFromFool(KEYSTORE, SSL_PASSWORD, AD_PASSWORD, LDAP_URL, 389);
}
构造用户对象用于测试
Attributes attrs = new BasicAttributes(true);
attrs.put("Title", "工程师");
attrs.put("Department", "部门");
attrs.put("Company", "机构");
attrs.put("objectClass","user");
attrs.put("userPrincipalName",userName + "@adserv.com");
 
  
 
  
 
  
 
  
 
  
 
  
 
 

你可能感兴趣的:(日常工作小结,Java,SE)