如何使用Apache LDAP API检测AD用户需下次登录时修改密码
你遇到的问题是AD返回的错误码49是通用凭证错误,具体原因存在子状态码中,Apache LDAP API默认的authenticate方法不会直接抛出「需要重置密码」的异常,需要你主动解析子状态码或查询用户属性判断。
方案1:解析AD返回的认证错误子状态码(最直接,不需要额外查询)
AD在返回49错误时,错误信息中会携带子状态码,其中773就对应用户需要下次登录时修改密码的场景(也就是pwdLastSet=0的情况)。你需要调整异常捕获逻辑,优先捕获LdapAuthenticationException,解析错误内容的子状态码即可判断。
方案2:认证失败后主动查询用户pwdLastSet属性(更稳妥,不受错误信息格式影响)
如果担心错误信息解析不稳定,可以在收到49错误后,用你配置的LDAP管理账号查询对应用户的pwdLastSet属性,若属性值为0即可判定需要重置密码。
修改后的代码示例:
public void authenticate(String uid, String password) { String status = ""; LdapConnectionTemplate ldapConnectionTemplate = null; try { LdapConnectionConfig config = new LdapConnectionConfig(); config.setUseSsl(true); config.setLdapHost("activedirectory.domain.net"); config.setLdapPort(636); config.setTrustManagers(new NoVerificationTrustManager()); config.setName(_ldapMgmtUser); config.setCredentials(_ldapMgmtPassword); final DefaultPoolableLdapConnectionFactory factory = new DefaultPoolableLdapConnectionFactory(config); final LdapConnectionPool pool = new LdapConnectionPool(factory); pool.setTestOnBorrow(true); ldapConnectionTemplate = new LdapConnectionTemplate(pool); final PasswordWarning warning = ldapConnectionTemplate.authenticate(_rootDn, "(sAMAccountName=" + uid + ")", SearchScope.SUBTREE, password.toCharArray()); status = "用户凭证认证成功"; if (warning != null) { status = status + " \n 警告:" + warning.toString(); } System.out.println(status); } catch (final LdapAuthenticationException authEx) { // 解析AD错误子状态码 String errorMsg = authEx.getMessage(); if (errorMsg.contains("data 773")) { status = "需要修改初始密码后再登录"; System.out.println(status); } else if (errorMsg.contains("data 52e")) { status = "用户名或密码错误"; System.err.println(status); } else if (errorMsg.contains("data 775")) { status = "账户已锁定"; System.err.println(status); } // 其他子状态码可按需扩展 authEx.printStackTrace(); } catch (final PasswordException e) { System.err.println("############# 密码相关异常 #############"); status = e.toString(); e.printStackTrace(); } catch (Exception e) { // 可选:补充查询pwdLastSet属性做二次校验 if (ldapConnectionTemplate != null) { Boolean needResetPwd = checkUserNeedResetPwd(uid, ldapConnectionTemplate); if (needResetPwd) { status = "需要修改初始密码后再登录"; System.out.println(status); } } System.err.println("############# 通用异常 #############"); e.printStackTrace(); } finally { } return; } // 辅助方法:查询用户是否需要重置密码 private Boolean checkUserNeedResetPwd(String uid, LdapConnectionTemplate ldapConnectionTemplate) { try { List<LdapEntry> entries = ldapConnectionTemplate.search(_rootDn, "(sAMAccountName=" + uid + ")", SearchScope.SUBTREE, "pwdLastSet"); if (entries.size() == 1) { LdapAttribute pwdLastSetAttr = entries.get(0).get("pwdLastSet"); if (pwdLastSetAttr != null && "0".equals(pwdLastSetAttr.getString())) { return true; } } } catch (Exception e) { e.printStackTrace(); } return false; }
注意事项
- 解析错误子状态码的逻辑要匹配你部署的AD版本返回的错误信息格式,大部分Windows Server版本的AD返回的错误信息都会携带
data 子码的格式。 - 查询
pwdLastSet属性需要LDAP管理账号有对应的属性读取权限,默认域管理员账号有权限读取该属性。
内容的提问来源于stack exchange,提问作者4integration
相关产品推荐
相关产品推荐

