龙空技术网

Java操作AD域修改密码和账号解锁功能

前端在路上 50

前言:

眼前兄弟们对“域用户怎么更改密码”大约比较注重,你们都想要知道一些“域用户怎么更改密码”的相关知识。那么小编同时在网络上收集了一些关于“域用户怎么更改密码””的相关内容,希望看官们能喜欢,大家快快来了解一下吧!

本文主要记录非证书方式操作,即绕开AD证书认证去修改密码和解锁账号

必备条件:一个能改密码和解锁账号的超级管理员账号;

跳过证书

需要用到这两个类DummySSLSocketFactory,DummyTrustManager,具体代码如下:

public class DummySSLSocketFactory extends SSLSocketFactory {    private SSLSocketFactory factory;        public DummySSLSocketFactory() {            try {                SSLContext sslcontext = SSLContext.getInstance("TLS");                sslcontext.init( null, // No KeyManager required                new TrustManager[] { new DummyTrustManager()},                new java.security.SecureRandom());                factory = ( SSLSocketFactory) sslcontext.getSocketFactory();                } catch( Exception ex) { ex.printStackTrace(); }        }        public static SocketFactory getDefault() {        return new DummySSLSocketFactory();        }        public Socket createSocket( Socket socket, String s, int i, boolean flag) throws IOException {        return factory.createSocket( socket, s, i, flag);        }        public Socket createSocket( InetAddress inaddr, int i, InetAddress inaddr1, int j) throws IOException {        return factory.createSocket( inaddr, i, inaddr1, j);        }        public Socket createSocket( InetAddress inaddr, int i) throws IOException {        return factory.createSocket( inaddr, i);        }        public Socket createSocket( String s, int i, InetAddress inaddr, int j) throws IOException {        return factory.createSocket( s, i, inaddr, j);        }        public Socket createSocket( String s, int i) throws IOException {        return factory.createSocket( s, i);        }        public String[] getDefaultCipherSuites() {        return factory.getSupportedCipherSuites();        }        public String[] getSupportedCipherSuites() {        return factory.getSupportedCipherSuites();        }}

可以直接贴去使用

public class DummyTrustManager implements X509TrustManager {    public void checkClientTrusted( X509Certificate[] cert, String authType) {        return;    }        public void checkServerTrusted( X509Certificate[] cert, String authType) {        return;    }        public X509Certificate[] getAcceptedIssuers() {        return new X509Certificate[0];    }}
连接LDAP

注意开通网络策略,这里用到是636这个端口。如果仅仅是AD认证,不需要使用这个端口

   private final static String adminName = "abc@qq.com";    private final static String adminPassword = "password";    private final String ldapURL = "LDAPS://10.192.30.119:636";   //注意,必须使用域名加636端口    private final String factory = "com.sun.jndi.ldap.LdapCtxFactory";    private final String factorySocket = "com.ztfsec.adservice.DummySSLSocketFactory";    private final String BASEN = "dc=test,dc=cn";    private LdapContext ctx = null;    private final String[] USER_DISABLED_STATUS = {"514", "546", "66050", "66080", "66082"};    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    private final Control[] sortConnCtls = new SortControl[1];    /**     * 用户认证     *     * @param userName     * @param password     */    public LdapContext ldapConnect(String userName, String password) {        //解决No subject alternative DNS name xxxxx的错误        Security.setProperty("jdk.tls.disabledAlgorithms", "");        System.setProperty("com.sun.jndi.ldap.object.disableEndpointIdentification", "true");        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", factorySocket);        try {            sortConnCtls[0] = new SortControl("sAMAccountName", Control.CRITICAL);            ctx = new InitialLdapContext(env, sortConnCtls);            return ctx;        } catch (Exception e) {            System.out.println(e);            e.printStackTrace();            return null;        }    }

这里ldapConnect传入的用户名和密码就是具备管理员的账号密码。但是这里选择参数传入,主要考虑的是普通用户执行重置密码前会做一次账号密码认证,只有输入的账号和原密码正确才允许进行改密操作。

   /**     * 查找用户信息     *     * @param userName     * @return     */    public Attributes getUser(String userName) {        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=" + userName, contro);            if (en == null || !en.hasMoreElements()) {                System.out.println("未找到该用户:" + userName);                return null;            }            while (en.hasMoreElements()) {                Object obj = en.nextElement();                if (obj instanceof SearchResult) {                    SearchResult si = (SearchResult) obj;                    attrs = si.getAttributes();                    //attrs是用户的一些相关属性,一些很重要的属性                }            }        } catch (NamingException e) {            System.out.println("查找用户异常。。。");            e.printStackTrace();        }        return attrs;    }

下面是解锁账号的操作

UserAccountControl属性

因为在修改AD域用户状态的时候发现,一些博客给定的禁用和启用用户的状态值并不一致,参考官网文档和国外的博客发现用户在禁用状态下,也会包含其他属性,这些属性有些是相加的。全量的属性列表如下:

如何参考并使用这个对照表呢?

可以搜索表中关于Disabled的行对应的十进制值,在程序中可以使用作为状态的判断——

UserAccountControl属性为514、546、66050、66080、66082里面的值的可以判断为禁用账号;

搜索Enabled,值为544、66048、262656的以及默认账户类型512可以判断为启用账户;

在开发中,现在的做法是:

disabled_user_flag = [514, 546, 66050, 66080, 66082] # 禁用账户的userAccountControl值列表

enabled_user_flag = [512, 544, 66048, 262656] # 启用账户

/**     * 解锁账号和下次登录需要修改密码     *     * @param userName     * @return 返回操作是否成功     */    public boolean enableUser(String userName) {        String userDN = getUserDN(userName);        BasicAttributes attrsbu = new BasicAttributes();        //这个是重点,下面有话有说        attrsbu.put("userAccountControl", "512");        attrsbu.put("lockoutTime", "0");        try {            ctx.modifyAttributes(userDN, DirContext.REPLACE_ATTRIBUTE, attrsbu);            System.out.println("解锁账号成功");            return true;        } catch (NamingException e) {            System.out.println("解锁账号失败");            e.printStackTrace();            return false;        }    }

判断账号是否被锁定

public boolean isLocked(String account) throws NamingException {        return isLocked(getUser(account));    }    private boolean isLocked(Attributes attrs) throws NamingException {        String userAccountControl = getInnerValue(attrs, "userAccountControl");        String lockoutTime = getInnerValue(attrs, "lockoutTime");        return userAccountControl != null && Arrays.asList(USER_DISABLED_STATUS).contains(userAccountControl) ||                lockoutTime != null && !lockoutTime.equals("0");    }

lockoutTime 大于0表示锁定,等于0是正常

/**     * 重置密码     *     * @param userName    用户名     * @param newPassword 新密码     * @return 返回操作是否成功     */    public boolean updateUserPassword(String userName, String newPassword) {        try {            ModificationItem[] mods = new ModificationItem[1];            String newQuotedPassword = "\"" + newPassword + "\"";            byte[] newUnicodePassword = newQuotedPassword.getBytes(StandardCharsets.UTF_16LE);            mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword));            // 修改密码            String userDN = getUserDN(userName);            ctx.modifyAttributes(userDN, mods);            return true;        } catch (Exception e) {            System.out.println(e);            return false;        }    }

获取密码策略

public class AdPasswordPolicy {    /**     * 密码最长使用期限, 单位天     */    private Long maxPwdAge;    /**     * 密码长度最小值     */    private Long minPwdLength;    /**     * 强制密码历史, 单位天     */    private Long pwdHistoryLength;    /**     * 账户锁定时间,单位秒     */    private Long lockoutDuration;    /**     * 账户锁定阈值     */    private Long lockoutThreshold;    /**     * 重置账户锁定计数器,单位秒     */    private Long lockOutObservationWindow;    public Long getMaxPwdAge() {        return (long) (-maxPwdAge * (0.000000100)) / 86400;    }    public void setMaxPwdAge(Long maxPwdAge) {        this.maxPwdAge = maxPwdAge;    }    public Long getMinPwdLength() {        return minPwdLength;    }    public void setMinPwdLength(Long minPwdLength) {        this.minPwdLength = minPwdLength;    }    public Long getPwdHistoryLength() {        return pwdHistoryLength;    }    public void setPwdHistoryLength(Long pwdHistoryLength) {        this.pwdHistoryLength = pwdHistoryLength;    }    public Long getLockoutDuration() {        return (long) (-lockoutDuration * (0.000000100));    }    public void setLockoutDuration(Long lockoutDuration) {        this.lockoutDuration = lockoutDuration;    }    public Long getLockoutThreshold() {        return lockoutThreshold;    }    public void setLockoutThreshold(Long lockoutThreshold) {        this.lockoutThreshold = lockoutThreshold;    }    public Long getLockOutObservationWindow() {        return (long) (-lockOutObservationWindow * (0.000000100));    }    public void setLockOutObservationWindow(Long lockOutObservationWindow) {        this.lockOutObservationWindow = lockOutObservationWindow;    }    @Override    public String toString() {        return "AdPasswordPolicy{" +                "maxPwdAge=" + getMaxPwdAge() +                ", minPwdLength=" + getMinPwdLength() +                ", pwdHistoryLength=" + getPwdHistoryLength() +                ", lockoutDuration=" + getLockoutDuration() +                ", lockoutThreshold=" + getLockoutThreshold() +                ", lockOutObservationWindow=" + getLockOutObservationWindow() +                '}';    }}
public AdPasswordPolicy getPolicy() {        try {            SearchControls controls = new SearchControls();            controls.setSearchScope(SearchControls.OBJECT_SCOPE);            String filter = "(&(objectClass=domainDNS))";            String[] returnedAttrs = {"minPwdAge", "maxPwdAge", "minPwdLength", "pwdHistoryLength", "lockoutDuration", "lockoutThreshold", "lockOutObservationWindow"};            // 设置返回属性集            controls.setReturningAttributes(returnedAttrs);            NamingEnumeration<SearchResult> results = ctx.search(BASEN, filter, controls);            AdPasswordPolicy passwordPolicy = null;            while (results.hasMore()) {                SearchResult searchResult = results.next();                Attributes attrs = searchResult.getAttributes();                Map<String, Long> map = MapUtil.newHashMap();                for (String key : returnedAttrs) {                    String keyVal = attrs.get(key).toString().split(":")[1].trim();                    map.put(key, Convert.toLong(keyVal));                }                passwordPolicy = BeanUtil.toBean(map, AdPasswordPolicy.class);            }            results.close();            return passwordPolicy;        } catch (NamingException e) {            return null;        }    }

标签: #域用户怎么更改密码 #域账户修改密码方法 #域账户密码怎么修改 #如何修改域用户密码 #如何修改域用户密码和密码