我正在开发一个网络应用,让用户可以在Active Directory中重置自己的密码。我通过以管理员身份绑定来完成它并且工作正常,但目录策略(重用历史记录,字符等)没有被强制执行。我不能以用户身份绑定,因为我没有当前的密码。
我读到了Windows 2008 R2 SP1中引入的LDAP_SERVER_POLICY_HINTS控件,用于在Active Directory中执行此操作,甚至找到了someone who made it using Spring LDAP
由于我使用的是UnboundID,并且没有提供标准控件,我认为我必须创建自己的控件类。记录的OID是1.2.840.113556.1.4.2239,值{48,3,2,1,1}
public class PolicyHintsControl extends Control {
private static final long serialVersionUID = 1L;
public final static String LDAP_SERVER_POLICY_HINTS_OID = "1.2.840.113556.1.4.2066";
public final static byte[] LDAP_SERVER_POLICY_HINTS_DATA = { 48,
(byte) 132, 0, 0, 0, 3, 2, 1, 1 };
public PolicyHintsControl() {
super(LDAP_SERVER_POLICY_HINTS_OID, false, new ASN1OctetString(
LDAP_SERVER_POLICY_HINTS_DATA));
}
@Override
public String getControlName() {
return "LDAP Server Policy Hints Control";
}
@Override
public void toString(StringBuilder buffer) {
buffer.append("LDAPServerPolicyHints(isCritical=");
buffer.append(isCritical());
buffer.append(')');
}
}
所以我在修改请求中添加了这个新控件,如下所示:
public static void main(String[] args) throws Exception {
final String host = "ldap.example.com";
final int port = 636;
String adminDn = "admin@example.com";
String adminPassword = "passwd";
String userDn = "CN=user,ou=people,dc=example,dc=com";
String userPassword = "passwd";
String keystoreFile = "/path/to/keystore.jks";
String keystorePassword = "passwd";
String passwordAttribute = "unicodePwd";
//Password change requires SSL
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(keystoreFile), keystorePassword.toCharArray());
TrustManagerFactory factory = TrustManagerFactory.getInstance("x509");
factory.init(keyStore);
final SSLUtil sslUtil = new SSLUtil(factory.getTrustManagers());
SSLSocketFactory socketFactory = sslUtil.createSSLSocketFactory();
Debug.setEnabled(true);
// Connect as the configured administrator
LDAPConnection ldapConnection = new LDAPConnection(socketFactory, host,
port, adminDn, adminPassword);
// Set password in AD format
final String newQuotedPassword = "\"" + userPassword + "\"";
final byte[] newPasswordBytes = newQuotedPassword.getBytes("UTF-16LE");
String encryptedNewPwd = new String(newPasswordBytes);
//Build modifications array and request
final ArrayList<Modification> modifications = new ArrayList<Modification>();
modifications.add(new Modification(ModificationType.REPLACE,
passwordAttribute, encryptedNewPwd));
ModifyRequest modifyRequest = new ModifyRequest(userDn, modifications);
//Add the policy hints control
modifyRequest.addControl(new PolicyHintsControl());
//Modify already
ldapConnection.modify(modifyRequest);
ldapConnection.close();
}
我得到以下异常:
Exception in thread "main" LDAPException(resultCode=53 (unwilling to perform), errorMessage='0000052D: SvcErr: DSID-031A120C, problem 5003 (WILL_NOT_PERFORM), data 0
', diagnosticMessage='0000052D: SvcErr: DSID-031A120C, problem 5003 (WILL_NOT_PERFORM), data 0
')
经过多方研究后,我发现Windows 2012中有另一个更新用于同一控件的更新,它将OID更改为1.2.840.113556.1.4.2066并弃用了旧的OID。
由于此应用程序可以配置任何版本的AD,我希望优雅地处理每个方案(Windows 2012,Windows 2008 R2 SP1,其他)。我的问题是:
答案 0 :(得分:3)
我并不熟悉微软特有的控件,所以我无法在那里提供很多帮助,但看起来你已经走上正轨了。在这种情况下,它实际上看起来控件正在按预期工作,服务器拒绝密码,因为它不够强大。
Active Directory非常糟糕,因为它很难解决这个问题,但秘诀在于诊断消息中给出的“0000052D”。这是对Active Directory系统错误代码0x52D的引用,它是十进制1325.系统错误代码记录在http://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx,在这种情况下,您需要遵循“系统错误代码(1300-1699)”链接( http://msdn.microsoft.com/en-us/library/windows/desktop/ms681385(v=vs.85).aspx)并找到值1325的说明。该错误代码的文本显示“无法更新密码。为新密码提供的值不符合域的长度,复杂性或历史记录要求。 “由于您尝试使用的控件点似乎是让服务器对新密码执行质量检查,看起来它正在按预期工作。如果您使用更强的密码(例如,使其更长,包括大写/数字/符号字符等),那么服务器可能会接受它。
关于确定服务器支持哪些控件的问题,这样做的方法是检索服务器根DSE并查看supportedControls属性中报告的OID。 UnboundID LDAP SDK for Java使这非常简单,因为您可以使用LDAPConnection.getRootDSE方法检索根DSE,然后使用RootDSE.supportsControl方法来确定服务器是否支持指定的控件。
关于是否处理具有相同类或不同类的不同OID的问题,这更多的是风格问题而不是其他问题。如果具有较新OID的控件也对该值使用不同的编码,那么这肯定会建议创建一个单独的类。如果两个OID的值编码相同,那么它可能是个人偏好的问题,但即使你将它们分成不同的类,那么最好保持大部分代码的通用,而不是在两个不同的地方使用相同的代码