сломит ответ на этот вопрос в три различные секции:
- Текущая проблема с вашей методологией
- вопросами с рекомендованными Solutions как онлайн, так и в этой теме
- Решение
Текущая проблема с вашей методологией.
NetValidatePasswordPolicy
требует своего параметра InputArgs
взять в указатель на структуру, а структура вы передаете в зависимости от ValidationType
вы как раз проходя. В этом случае, вы передаете NET_VALIDATE_PASSWORD_TYPE.NetValidateAuthentication
, что требует InputArgs из NET_VALIDATE_AUTHENTICATION_INPUT_ARG
но вы передаете указатель на NET_VALIDATE_PASSWORD_CHANGE_INPUT_ARG
.
Кроме того, вы пытаетесь присвоить «тип currentPassword» ценности для NET_VALIDATE_PASSWORD_CHANGE_INPUT_ARG
структуры.
Однако есть большая фундаментальная proble к использованию NetValidatePasswordPolicy
и что это то, что вы пытаетесь использовать эту функцию для проверки паролей в Active Directory, но это не то, что он используется для. NetValidatePasswordPolicy
используется, чтобы позволить приложения для проверки по базе данных аутентификации, предоставляемых приложением.
Там еще информации о NetValidatePasswordPolicy
here.
Вопросы рекомендуемого решения как в Интернете, так и в этой теме
Различные статьи в Интернете рекомендуется использовать функцию LogonUser
найти в AdvApi32.dll
, но эта реализация носит свой собственный набор вопросов:
Первый является что LogonUser
проверяет локальный кеш, а это означает, что вы не получите мгновенную точную информацию об учетной записи, если только вы не используете режим «Сеть».
Во-вторых, использование LogonUser
в веб-приложении, на мой взгляд, немного хакерское, так как оно предназначено для настольных приложений, работающих на клиентских компьютерах. Однако, учитывая ограничения, предоставленные Microsoft, если LogonUser
дает желаемые результаты, я не понимаю, почему его не следует использовать - запрет на проблемы кэширования.
Другая проблема с LogonUser
заключается в том, насколько хорошо он работает в вашем случае использования, зависит от того, как настроен ваш сервер, например: Существуют определенные разрешения, которые необходимо включить в домене, который вы аутентифицируете, для работы в режиме «Сетевой».
Дополнительная информация о LogonUser
here.
Кроме того, не следует использовать, вместо этого следует использовать GetLastWin32Error()
, так как небезопасно использовать .
Дополнительная информация о GetLastWin32Error()
here.
Решение.
Чтобы получить точный код ошибки в Active Directory, без каких-либо проблем с кешированием и прямо из служб каталогов, это то, что нужно сделать: полагаться на COMException, возвращающийся из AD, когда есть проблема с учетной записью, потому что, в конечном счете, ошибки - это то, что вы ищете.
Во-первых, вот как вызвать ошибку из Active Directory аутентификации текущего имени пользователя и пароля:
public LdapBindAuthenticationErrors AuthenticateUser(string domain, string username, string password, string ouString)
{
// The path (ouString) should not include the user in the directory, otherwise this will always return true
DirectoryEntry entry = new DirectoryEntry(ouString, username, password);
try
{
// Bind to the native object, this forces authentication.
var obj = entry.NativeObject;
var search = new DirectorySearcher(entry) { Filter = string.Format("({0}={1})", ActiveDirectoryStringConstants.SamAccountName, username) };
search.PropertiesToLoad.Add("cn");
SearchResult result = search.FindOne();
if (result != null)
{
return LdapBindAuthenticationErrors.OK;
}
}
catch (DirectoryServicesCOMException c)
{
LdapBindAuthenticationErrors ldapBindAuthenticationError = -1;
// These LDAP bind error codes are found in the "data" piece (string) of the extended error message we are evaluating, so we use regex to pull that string
if (Regex.Match(c.ExtendedErrorMessage, @" data (?<ldapBindAuthenticationError>[a-f0-9]+),").Success)
{
string errorHexadecimal = match.Groups["ldapBindAuthenticationError"].Value;
ldapBindAuthenticationError = (LdapBindAuthenticationErrors)Convert.ToInt32(errorHexadecimal , 16);
return ldapBindAuthenticationError;
}
catch (Exception e)
{
throw;
}
}
return LdapBindAuthenticationErrors.ERROR_LOGON_FAILURE;
}
И это ваши «LdapBindAuthenticationErrors», вы можете найти больше в MSDN, here.
internal enum LdapBindAuthenticationErrors
{
OK = 0
ERROR_INVALID_PASSWORD = 0x56,
ERROR_PASSWORD_RESTRICTION = 0x52D,
ERROR_LOGON_FAILURE = 0x52e,
ERROR_ACCOUNT_RESTRICTION = 0x52f,
ERROR_INVALID_LOGON_HOURS = 0x530,
ERROR_PASSWORD_EXPIRED = 0x532,
ERROR_ACCOUNT_DISABLED = 0x533,
ERROR_ACCOUNT_EXPIRED = 0x701,
ERROR_PASSWORD_MUST_CHANGE = 0x773,
ERROR_ACCOUNT_LOCKED_OUT = 0x775
}
Затем вы можете использовать возвращаемый тип этого Enum и делать то, что вам нужно с ним в вашем контроллере. Важно отметить, что вы ищете фрагмент «данных» строки в «Расширенном сообщении об ошибке» вашего COMException
, потому что в нем содержится всемогущий код ошибки, который вы ищете.
Удачи, и я надеюсь, что это поможет. Я тестировал его, и он отлично работает для меня.
Вы пробовали GetLastError() после ValidateCredentials()? то вы можете использовать код ошибки для определения сообщения, например, 1907 «пользователь должен сбросить пароль», 1330 «истек срок действия пароля» и т. д. ... другой вариант, о котором я могу думать, - [Logon] (http: // www.pinvoke.net/default.aspx/advapi32/LogonUser.html) вместо ValidateCredentials, а затем GetLastError() –
Я просмотрел исходный код ValidateCredentials(), метод использует LADP, если есть код ошибки, метод будет генерировать исключение, а затем поймать исключение и вернуть false. поэтому GetLastError, вероятно, будет работать. Я также видел, что есть специальные коды ошибок для активного каталога. –
Мы не хотели использовать метод LogonUser, потому что мы используем службы каталогов для входа в систему. Если пользователь ошибочно вводит свой пароль, мы попытаемся выполнить вход в систему, выйти из системы, а затем войти в систему, чтобы узнать, почему это не удалось. Это дало бы две неудачные попытки входа в систему для каждой попытки пользователя. Я посмотрю на GetLastError(), но кажется странным, что ValidateCredentials будет возвращать 1907 каждый раз, а затем заставит разработчика посмотреть GetLastError(), чтобы получить фактический код. – johnnywhoop