JAVA JNDI免证书修改AD域密码

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域密码

上一篇:docker安装nacos以及配置数据库


下一篇:docker 安装nacos