前段时间,我接到了一个新任务:使用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;
}
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;
}
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----------------------------------------
有读者说有一部分代码没有发出来,拉下了不好意思,现在补上
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;
}
/**
* 获取连接通道,如果是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");