Java通过JNDI的方式修改LDAP的密码时需要证书,而使用证书比较繁琐,一大堆复杂的操作导出什么密钥库之类的。且证书1年就失效了还需要企业根证书才行,很多限制!以下的代码为无证书方式修改AD域密码,测试过能够正常修改密码,其关键是配置了DummySSLSocketFactory类,其它相关的说明在代码中已经注明,DummySSLSocketFactory的代码可在下方下载到。
package com.cjzj.ad.util;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AdUtil {
private static Logger log = LoggerFactory.getLogger(AdUtil.class);
// AD根目录
private final String BASEN = "DC=cjzj,DC=com";
ThreadLocal<DirContext> ctxThreadLocal = new ThreadLocal<DirContext>();
private Properties initSSLEnv() {
Properties env = new Properties();
env.put(Context.AUTHORITATIVE, "true");
// 修改密码需使用SSL协议
env.put(Context.SECURITY_PROTOCOL, "ssl");
env.put(DirContext.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
// 免证书修改AD密码指定的类
env.put("java.naming.ldap.factory.socket", "com.cjzj.ad.DummySSLSocketFactory");
// 拥有Domain Admins角色的用户,域管理员
env.put(Context.SECURITY_PRINCIPAL, "cjzj");
env.put(Context.SECURITY_CREDENTIALS, "cjzj123$%");
// 修改时要使用636端口
env.put(Context.PROVIDER_URL, "ldap://192.168.90.31:636");
// env.put("com.sun.jndi.ldap.connect.timeout", connectTimeout);
// Enable connection pooling
// env.put("com.sun.jndi.ldap.connect.pool", connectPool);
// env.put("com.sun.jndi.ldap.connect.pool.timeout", connectPoolTimeout);
return env;
}
private boolean modifyPwd(String username, String newPassword) {
boolean result = false;
Properties env = null;
try {
env = initSSLEnv();
// 登录
ctxThreadLocal.set(new InitialDirContext(env));
log.debug(env.getProperty(Context.SECURITY_PRINCIPAL) + " authenticate is ok!");
ModificationItem[] mods = new ModificationItem[1];
String newQuotedPassword = "\"" + newPassword + "\"";
byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
new BasicAttribute("unicodePwd", newUnicodePassword));
// 修改密码
String userDN = getUserDN(username);
ctxThreadLocal.get().modifyAttributes(userDN, mods);
closeContext();
env.clear();
env = null;
log.debug(username + " modify password is success!");
result = true;
} catch (Exception e) {
e.printStackTrace();
log.warn("username or password is incorrect!", e);
} finally {
closeContext();
if (env != null) {
try {
env.clear();
env = null;
} catch (Exception e) {
}
}
}
return result;
}
/**
* 获取用户的dn
*
* @param username
* @return
*/
public String getUserDN(String username) {
Attributes attrs = getUser(username);
// distinguishedname这个属性即是用户的dn,可以打印看看
String userDN = attrs.get("distinguishedname").toString().split(":")[1].trim();
return userDN;
}
/**
* 查找用户信息
*
* @param cn
* @return
*/
public Attributes getUser(String cn) {
Attributes attrs = null;
SearchControls contro = new SearchControls();
contro.setSearchScope(SearchControls.SUBTREE_SCOPE);
try {
// 有的企业员工的dn不是有cn开头的,而是由uid开头的,这个因企业而异
// 使用cn,若存在重名用户,则返回的是最后一个员工,存在bug
// NamingEnumeration en = ctx.search(BASEN, "cn=" + cn, contro);
// 使用sAMAccountName,避免重名,比如存在四个张伟
NamingEnumeration<SearchResult> en = ctxThreadLocal.get().search(BASEN, "sAMAccountName=" + cn, contro);
if (en == null || !en.hasMoreElements()) {
System.out.println("未找到该用户:" + cn);
return null;
}
while (en.hasMoreElements()) {
Object obj = en.nextElement();
if (obj instanceof SearchResult) {
SearchResult si = (SearchResult) obj;
attrs = si.getAttributes();
// attrs是用户的一些相关属性,一些很重要的属性
System.out.println(attrs);
}
}
} catch (NamingException e) {
System.out.println("查找用户异常。。。");
e.printStackTrace();
}
return attrs;
}
// 关闭连接
public void closeContext() {
try {
if (ctxThreadLocal.get() != null) {
ctxThreadLocal.get().close();
ctxThreadLocal.set(null);
}
} catch (NamingException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
AdUtil adUtil = new AdUtil();
if (adUtil.modifyPwd("weisunqing", "abc123$%")) {
System.out.println("修改成功");
}
}
}
代码下载:JAVA JNDI免证书修改AD域密码