前言:
眼前兄弟们对“域用户怎么更改密码”大约比较注重,你们都想要知道一些“域用户怎么更改密码”的相关知识。那么小编同时在网络上收集了一些关于“域用户怎么更改密码””的相关内容,希望看官们能喜欢,大家快快来了解一下吧!本文主要记录非证书方式操作,即绕开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; } }
标签: #域用户怎么更改密码 #域账户修改密码方法 #域账户密码怎么修改 #如何修改域用户密码 #如何修改域用户密码和密码