我正在研究一个简单的解决方案来更新 Active Directory 中的用户密码。
我可以成功更新用户密码。更新密码工作正常。假设用户已将密码从 MyPass1 更新为 MyPass2
现在,当我运行自定义代码以使用以下方法验证用户凭据时:
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "TheDomain"))
{
// validate the credentials
bool isValid = pc.ValidateCredentials("myuser", "MyPass2");
}
//returns true - which is good
现在,当我输入一些错误的密码时,它会很好地验证:
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "TheDomain"))
{
// validate the credentials
bool isValid = pc.ValidateCredentials("myuser", "wrongPass");
}
//returns false - which is good
现在由于一些奇怪的原因,它验证了之前的最后一个密码,也就是 MyPass1,还记得吗?
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "TheDomain"))
{
// validate the credentials
bool isValid = pc.ValidateCredentials("myuser", "MyPass1");
}
//returns true - but why? we have updated password to Mypass2
我从以下位置获得此代码:
Validate a username and password against Active Directory?
这是否与上次密码过期有关,或者验证应该如何工作?
最佳答案
你看到这个的原因与 special behavior specific to NTLM network authentication 有关.
在 PrincipalContext
实例上调用 ValidateCredentials
方法会导致建立安全的 LDAP 连接,然后使用 ldap_bind_s 在该连接上执行绑定(bind)操作
函数调用。
调用ValidateCredentials
时使用的身份验证方法是AuthType.Negotiate
。使用它会导致尝试使用 Kerberos 进行绑定(bind)操作,Kerberos(当然不是 NTLM)不会表现出上述特殊行为。但是,使用 Kerberos 的绑定(bind)尝试将失败(密码错误和全部),这将导致进行另一次尝试,这次使用 NTLM。
您有两种方法可以解决此问题:
- 按照我链接的 Microsoft 知识库文章中的说明使用 OldPasswordAllowedPeriod 注册表值缩短或消除旧密码的生命周期。可能不是最理想的解决方案。
- 不要使用
PrincipleContext
类来验证凭据。既然您已经(大致)了解了ValidateCredentials
的工作原理,那么手动完成该过程应该不会太困难。您要做的是创建一个新的 LDAP 连接 (LdapConnection
),设置其网络凭据,将 AuthType 显式设置为AuthType.Kerberos
,然后调用绑定(bind)()
。如果凭据错误,您将得到一个异常(exception)。
以下代码显示了如何仅使用 Kerberos 执行凭据验证。如果失败,所使用的身份验证方法将不会回退到 NTLM。
private const int ERROR_LOGON_FAILURE = 0x31;
private bool ValidateCredentials(string username, string password, string domain)
{
NetworkCredential credentials
= new NetworkCredential(username, password, domain);
LdapDirectoryIdentifier id = new LdapDirectoryIdentifier(domain);
using (LdapConnection connection = new LdapConnection(id, credentials, AuthType.Kerberos))
{
connection.SessionOptions.Sealing = true;
connection.SessionOptions.Signing = true;
try
{
connection.Bind();
}
catch (LdapException lEx)
{
if (ERROR_LOGON_FAILURE == lEx.ErrorCode)
{
return false;
}
throw;
}
}
return true;
}
我尽量不使用异常来处理代码的流程控制;但是,在这个特定的实例中,测试 LDAP 连接上的凭据的唯一方法似乎是尝试绑定(bind)操作,如果凭据错误,这将引发异常。 PrincipalContext
采用相同的方法。
关于c# - 为什么 Active Directory 会验证最后一个密码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8949501/