• JAVA修改AD域密码_免证书认证


    更改用户账户密码,必须要使用ssl方式登录到AD。

    网上大部分教程使用TrustStore的方式连接, 

            Hashtable env = new Hashtable();
            System.setProperty("javax.net.ssl.trustStore", KEYSTORE);
            System.setProperty("javax.net.ssl.trustStorePassword", KEYSTORE_PWD);
            env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            env.put(Context.SECURITY_AUTHENTICATION, "simple");
            env.put(Context.SECURITY_PRINCIPAL, ADMIN_NAME);
            env.put(Context.SECURITY_CREDENTIALS, ADMIN_PASSWORD);
            env.put(Context.SECURITY_PROTOCOL, "ssl");
            env.put(Context.PROVIDER_URL, LDAP_SSL_URL);
    try {
                ctx =  new InitialLdapContext(env, null);//new InitialDirContext(HashEnv);// 初始化上下文
            } catch (AuthenticationException e) {
                System.out.println("身份验证失败!"+e.toString());
                e.printStackTrace();
            } catch (CommunicationException e) {
                System.out.println("AD域连接失败!"+e.toString());
                e.printStackTrace();
            } catch (Exception e) {
                System.out.println("身份验证未知异常!"+e.toString());
                e.printStackTrace();
            } finally{
                return ctx;
            }  

    最后ssl连接失败,报如下错误:javax.naming.CommunicationException: simple bind failed: xxxx:636 [Root exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target]。

    也有部分教程提到了绕过ssl的方式,但是都只有部分代码,直到遇到了这段代码

    原地址:JAVA修改AD域密码_免证书

    下载地址:JNDI免证书推送AD域密码.zip

    重要备注:虽然该方式可以避免使用TrustStore认证的方式,但是ad域控服务器仍然需要添加信任证书,如下

     且该证书对应的域名必须和AD域控所管理的域保持一致(也可能是AD域对应的证书服务器颁发的证书,我们的域控恰好是对应的证书服务器),域控处于dccn.com, 则证书不能使用例如abc.test.com,否则会报DNS异常如下:

    javax.naming.CommunicationException: simple bind failed: cdt.xxx.cn:636 [Root exception is javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative DNS name matching cdstest.huanghongbo.cn found.]

    完整代码如下:

    package org.util.ad;
    
    import javax.naming.Context;
    import javax.naming.NamingEnumeration;
    import javax.naming.NamingException;
    import javax.naming.directory.*;
    import javax.naming.ldap.Control;
    import javax.naming.ldap.InitialLdapContext;
    import javax.naming.ldap.LdapContext;
    import javax.naming.ldap.SortControl;
    import java.io.UnsupportedEncodingException;
    import java.util.Calendar;
    import java.util.Date;
    import java.util.Hashtable;
    
    public class LdapSSLUtil {
        //ldap用户登录的3种方式
    //方式1. 域account
    private final static String adminName = "tylincn02\tylinoap";
    //方式2. account@域
    // private final static String adminName = "tylinoap@tylincn.com";
    //方式3. cn用户(网上最常见的方式可能会有重名的情况,不适用于用户查找,不推荐)
    // private final static String adminName = "cn=tylinoap,OU=ITUSER,DC=tylincn,DC=com";

    private final static String adminName = "xxx";
        private final static String adminPassword = "xxxx";
        private final String ldapURL = "LDAPS://xxx.xxx.xxx:636";   //注意,必须使用域名加636端口
        private final String factory = "com.sun.jndi.ldap.LdapCtxFactory";
        private final String BASEN = "OU=xx,DC=xx,DC=xx";
        private LdapContext ctx = null;
    
        private final Control[] sortConnCtls = new SortControl[1];
    
    
        /**
         * 用户认证
         *
         * @param userName
         * @param password
         */
        public void ldap_connect(String userName, String password) {
    
    
            Hashtable<String, Object> env = new Hashtable<String, Object>();
            env.put(Context.INITIAL_CONTEXT_FACTORY, factory);
            env.put(Context.SECURITY_AUTHENTICATION, "simple");
            env.put(Context.SECURITY_PRINCIPAL, userName);
            env.put(Context.SECURITY_CREDENTIALS, password);
            env.put(Context.PROVIDER_URL, ldapURL);
            env.put(Context.SECURITY_PROTOCOL, "ssl");
            env.put("java.naming.ldap.factory.socket", "org.util.ad.DummySSLSocketFactory");
    
    
            try {
    
                // 初始化ldapcontext,方式1
    //            ctx = new InitialLdapContext(env, null);
                //方式2
                sortConnCtls[0] = new SortControl("sAMAccountName", Control.CRITICAL);
                ctx = new InitialLdapContext(env, sortConnCtls);
    
                System.out.println("认证成功");
            } catch (Exception e) {
                System.out.println("认证失败");
                e.printStackTrace();
            }
        }
    
        //关闭连接
        public void close_connect() {
            try {
                if (ctx != null) {
                    ctx.close();
                }
            } catch (NamingException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 查找用户信息
         *
         * @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<SearchResult> en = ctx.search(BASEN, "cn=" + cn, contro);
                //使用sAMAccountName,避免重名,比如存在四个张伟
                NamingEnumeration<SearchResult> en = ctx.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;
        }
    
        /**
         * 获取用户的dn
         *
         * @param cn
         * @return
         */
        public String getUserDN(String cn) {
            Attributes attrs = getUser(cn);
            //distinguishedname这个属性即是用户的dn,可以打印看看
            String userDN = attrs.get("distinguishedname").toString().split(":")[1].trim();
            return userDN;
        }
    
        //解锁账号和下次登录需要修改密码
        public void enableUser(String userName) {
            String userDN = getUserDN(userName);
            BasicAttributes attrsbu = new BasicAttributes();
    
            //这个是重点,下面有话有说
            attrsbu.put("userAccountControl", "512");
            attrsbu.put("pwdLastSet", "0");
    
            try {
                ctx.modifyAttributes(userDN, DirContext.REPLACE_ATTRIBUTE, attrsbu);
                System.out.println("解锁账号成功");
            } catch (NamingException e) {
                System.out.println("解锁账号失败");
                e.printStackTrace();
            }
        }
    
        //重置密码
        public void updateUserPassword(String cn, String newPassword) throws
                UnsupportedEncodingException, NamingException {
    
            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(cn);
            ctx.modifyAttributes(userDN, mods);
    
        }
    
        /**
         * AD账户时间戳转换
         * @param accountExpiresL
         * @return
         */
        public static Date adExpiresToDate(long accountExpiresL){
            Calendar calendar = Calendar.getInstance();
            calendar.clear();
            calendar.set(1601, 0, 1, 0, 0);
            accountExpiresL = accountExpiresL/ 10000 + calendar.getTime().getTime();
            return new Date(accountExpiresL);
        }
    
        /**
         * 获取AD账户失效日期
         * @param account
         * @return
         */
        public  Date getAccountExpiresToDate(String account){
            Attributes attrs = getUser(account);
            //distinguishedname这个属性即是用户的dn,可以打印看看
            String accountexpires = attrs.get("accountexpires").toString().split(":")[1].trim();
    //        return accountexpires;
            System.out.println(accountexpires);
            Long expiresLong = Long.parseLong(accountexpires);
            Date expiresDate  = adExpiresToDate(expiresLong);
            System.out.println(expiresDate);
            return expiresDate;
        }
    
        /**
         * 上次密码修改时间
         * @param account
         * @return
         */
        public Date getPwdLastSetTime(String account){
            Attributes attrs = getUser(account);
            //distinguishedname这个属性即是用户的dn,可以打印看看
            String pwdlastset = attrs.get("pwdlastset").toString().split(":")[1].trim();
    //        return accountexpires;
            System.out.println(pwdlastset);
            Long pwdlastsetLong = Long.parseLong(pwdlastset);
            Date pwdLastSetDate  = adExpiresToDate(pwdlastsetLong);
            System.out.println(pwdLastSetDate);
            return pwdLastSetDate;
        }
    
        public static void main(String[] args)throws Exception{
            LdapSSLUtil ldapUtil = new LdapSSLUtil();
            ldapUtil.ldap_connect(adminName, adminPassword);
            String account = "xxx";
            //重置密码
            ldapUtil.updateUserPassword(account,"xxxx");
            ldapUtil.getUserDN(account);
            ldapUtil.getAccountExpiresToDate(account);
            ldapUtil.getPwdLastSetTime(account);
            
            ldapUtil.close_connect();
    
        }
    }

     关于AD用户

    参考:

    https://blog.csdn.net/xuxiaoqun0_0/article/details/82052218

    https://blogs.msdn.microsoft.com/alextch/2012/05/15/how-to-set-active-directory-password-from-java-application/

    https://www.iteye.com/blog/chnic-2065877

    https://my.oschina.net/haison/blog/678354?p={{page}}

    https://blog.csdn.net/laxsong/article/details/51344002

    https://www.cnblogs.com/sunjiguang/p/9257585.html

    https://www.bbsmax.com/A/q4zVZ0nGzK/

    https://www.cnblogs.com/nidongde/p/5364622.html

    https://blog.csdn.net/hc1017/article/details/81293323?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

    https://blog.csdn.net/qq_41207282/article/details/97133887#comments

    https://blog.csdn.net/hct368/article/details/97247258

    https://www.cnblogs.com/amoyzhu/p/9259264.html

    https://blog.51cto.com/gaowenlong/1969586

    https://blog.51cto.com/gaowenlong/1969585

  • 相关阅读:
    SQL练习题28:创建一个actor表,包含如下列信息(注:sqlite获取系统默认时间是datetime('now','localtime'))
    SQL练习题27:你能使用子查询的方式找出属于Action分类的所有电影对应的title,description吗
    SQL练习题26: 将employees表的所有员工的last_name和first_name拼接起来作为Name,中间以一个空格区分 (注:该数据库系统是sqllite,字符串拼接为 || 符号,不支持concat函数)
    SQL练习题25:使用join查询方式找出没有分类的电影id以及名称
    SQL练习题24:给出每个员工每年薪水涨幅超过5000的员工编号emp_no、薪水变更开始日期from_date以及薪水涨幅值salary_growth,并按照salary_growth逆序排列。 (数据保证每个员工的每条薪水记录to_date-from_date=1年,而且同一员工的下一条薪水记录from_data=上一条薪水记录的to_data)
    SQL练习题23:汇总各个部门当前员工的title类型的分配数目,即结果给出部门编号dept_no、dept_name、其部门下所有的当前(dept_emp.to_date = '9999-01-01')员工的当前(titles.to_date = '9999-01-01')title以及该类型title对应的数目count
    SQL练习题23:获取员工其当前的薪水比其manager当前薪水还高的相关信息,当前表示to_date='9999-01-01', 结果第一列给出员工的emp_no, 第二列给出其manager的manager_no, 第三列给出该员工当前的薪水emp_salary, 第四列给该员工对应的manager当前的薪水manager_salary
    SQL练习题22:获取所有非manager员工当前的薪水情况,给出dept_no、emp_no以及salary ,当前表示to_date='9999-01-01'
    SQL练习题22:查找所有员工自入职以来的薪水涨幅情况,给出员工编号emp_no以及其对应的薪水涨幅growth,并按照growth进行升序
    SQL练习题21:查找当前薪水(to_date='9999-01-01')排名第二多的员工编号emp_no、薪水salary、last_name以及first_name,你可以不使用order by完成吗
  • 原文地址:https://www.cnblogs.com/huanghongbo/p/12409209.html
Copyright © 2020-2023  润新知