Причина, по которой вы видите это, связана с особым поведением, специфичным для сетевой аутентификации NTLM .
Вызов метода ValidateCredentials
в экземпляре PrincipalContext
приводит к безопасномуСоединение LDAP устанавливается, после чего выполняется операция связывания этого соединения с использованием вызова функции ldap_bind_s
.
Метод аутентификации, используемый при вызове ValidateCredentials
: AuthType.Negotiate
.Использование этого приводит к попытке операции связывания с использованием Kerberos, которая (будучи, конечно, , а не NTLM) не будет демонстрировать особого поведения, описанного выше.Однако попытка связывания с использованием Kerberos завершится неудачно (неверный пароль и все), что приведет к другой попытке, на этот раз с использованием NTLM.
У вас есть два способа приблизиться к этому:
- Следуйте инструкциям в статье Microsoft KB, которую я связал, чтобы сократить или исключить срок жизни старого пароля, используя значение реестра OldPasswordAllowedPeriod .Наверное, не самое идеальное решение.
- Не используйте
PrincipleContext
класс для проверки учетных данных.Теперь, когда вы (примерно) знаете, как работает ValidateCredentials
, вам не составит труда выполнить этот процесс вручную.Вам нужно будет создать новое соединение LDAP (LdapConnection
), установить его сетевые учетные данные, явно установить AuthType на AuthType.Kerberos
, а затем вызвать Bind()
.Вы получите исключение, если учетные данные плохие.
В следующем коде показано, как выполнить проверку учетных данных, используя только 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
использует тот же подход.