Как я могу проверить имя пользователя и пароль в Active Directory? Я просто хочу проверить правильность имени пользователя и пароля.





Вероятно, самый простой способ - это PInvoke LogonUser Win32 API. Например.
Ссылка MSDN здесь ...
Определенно хочу использовать тип входа
LOGON32_LOGON_NETWORK (3)
Это создает только легкий токен - идеально подходит для проверок AuthN. (другие типы могут использоваться для создания интерактивных сеансов и т. д.)
@cciotti: Нет, это неправильно. ЛУЧШИЙ способ правильно аутентифицировать кого-то - использовать LogonUserAPI как @stephbu write. Все остальные методы, описанные в этом посте, НЕ РАБОТАЮТ на 100%. Замечу, однако, я считаю, что вы должны быть присоединены к домену, чтобы вызвать LogonUser.
@Alan, чтобы сгенерировать учетные данные, вы должны иметь возможность подключиться к домену, передав действующую учетную запись домена. Однако я почти уверен, что ваша машина не обязательно должна быть членом домена.
API LogonUser требует, чтобы у пользователя был уровень Действовать как часть операционной системы; это не то, что получают пользователи, и не то, что вы хотите предоставлять каждому пользователю в организации. (msdn.microsoft.com/en-us/library/aa378184(v=vs.85).aspx)
Согласен Иэн, только LogonUser и SSPI могут отображать весь спектр возможных результатов проверки кредита. Выполнение SSPI на C#, как того требует OP, далеко не просто, поэтому рекомендуется LogonUser. Вы видели библиотеку, которая элегантно объединяет ее, не прибегая к неуправляемому коду. Я не смотрел ТБХ.
LogonUser требует только Действовать как часть операционной системы для Windows 2000 и ниже в соответствии с support.microsoft.com/kb/180548 ... Он выглядит чистым для Server 2003 и выше.
LogonUser не будет работать, если домен не является доменом, которому вы доверяете. Если вы хотите проверить учетные данные домена, вам нужно будет выполнить привязку к LDAP-серверу AD.
Попробуйте этот код (ПРИМЕЧАНИЕ: Сообщается, что не работает на Windows Server 2000)
#region NTLogonUser
#region Direct OS LogonUser Code
[DllImport( "advapi32.dll")]
private static extern bool LogonUser(String lpszUsername,
String lpszDomain, String lpszPassword, int dwLogonType,
int dwLogonProvider, out int phToken);
[DllImport("Kernel32.dll")]
private static extern int GetLastError();
public static bool LogOnXP(String sDomain, String sUser, String sPassword)
{
int token1, ret;
int attmpts = 0;
bool LoggedOn = false;
while (!LoggedOn && attmpts < 2)
{
LoggedOn= LogonUser(sUser, sDomain, sPassword, 3, 0, out token1);
if (LoggedOn) return (true);
else
{
switch (ret = GetLastError())
{
case (126): ;
if (attmpts++ > 2)
throw new LogonException(
"Specified module could not be found. error code: " +
ret.ToString());
break;
case (1314):
throw new LogonException(
"Specified module could not be found. error code: " +
ret.ToString());
case (1326):
// edited out based on comment
// throw new LogonException(
// "Unknown user name or bad password.");
return false;
default:
throw new LogonException(
"Unexpected Logon Failure. Contact Administrator");
}
}
}
return(false);
}
#endregion Direct Logon Code
#endregion NTLogonUser
за исключением того, что вам нужно будет создать собственное исключение для «LogonException»
Не используйте обработку исключений для возврата информации из метода. «Неизвестное имя пользователя или неверный пароль» не является исключением, это стандартное поведение для LogonUser. Просто верните false.
да ... это был порт из старой библиотеки VB6 ... написанной в 2003 году или около того ... (когда впервые появился .Net)
При запуске в Windows 2000 этот код не будет работать (support.microsoft.com/kb/180548)
Переосмысление этого. Ожидаемое поведение пользователя при входе в систему, его цель - войти в систему. Если ему не удается выполнить эту задачу, это ЯВЛЯЕТСЯ исключение. Фактически, метод должен возвращать void, а не логическое значение. Кроме того, если вы только что вернули логическое значение, у потребителя метода не будет возможности сообщить пользователю, в чем причина сбоя.
Мы делаем это в нашем Интранете
Вы должны использовать System.DirectoryServices;
Вот внутренности кода
using (DirectoryEntry adsEntry = new DirectoryEntry(path, strAccountId, strPassword))
{
using (DirectorySearcher adsSearcher = new DirectorySearcher(adsEntry))
{
//adsSearcher.Filter = "(&(objectClass=user)(objectCategory=person))";
adsSearcher.Filter = "(sAMAccountName = " + strAccountId + ")";
try
{
SearchResult adsSearchResult = adsSearcher.FindOne();
bSucceeded = true;
strAuthenticatedBy = "Active Directory";
strError = "User has been authenticated by Active Directory.";
}
catch (Exception ex)
{
// Failed to authenticate. Most likely it is caused by unknown user
// id or bad strPassword.
strError = ex.Message;
}
finally
{
adsEntry.Close();
}
}
}
Что вы вкладываете в «путь»? Имя домена? Имя сервера? Путь LDAP к домену? Путь LDAP к серверу?
Ответ1: Нет, мы запускаем его как веб-службу, поэтому его можно вызывать из разных мест в основном веб-приложении. Ответ2: Путь содержит информацию LDAP ... LDAP: // DC = domainname1, DC = domainname2, DC = com
Казалось бы, это может позволить инъекцию LDAP. Возможно, вы захотите избежать или удалить скобки в strAccountId
Означает ли это, что strPassword хранится в LDAP в виде обычного текста?
Никогда не должно быть необходимости явно вызывать Close() для переменной using.
Мне нужно проверить, а затем получить некоторую информацию о подтвержденном пользователе. этот код работает для меня. Спасибо
используйте путь, например entry.Path = @ "LDAP: // OU = " "Пользователи компании" ", OU = Company, DC = boise, DC = esiconstruction, DC = com"; Путь идет справа налево, где крайняя правая организационная единица является верхним узлом.
очень простое решение с использованием DirectoryServices:
using System.DirectoryServices;
//srvr = ldap server, e.g. LDAP://domain.com
//usr = user name
//pwd = user password
public bool IsAuthenticated(string srvr, string usr, string pwd)
{
bool authenticated = false;
try
{
DirectoryEntry entry = new DirectoryEntry(srvr, usr, pwd);
object nativeObject = entry.NativeObject;
authenticated = true;
}
catch (DirectoryServicesCOMException cex)
{
//not authenticated; reason why is in cex
}
catch (Exception ex)
{
//not authenticated due to some other exception [this is optional]
}
return authenticated;
}
доступ к NativeObject требуется для обнаружения неверного пользователя / пароля
Этот код плох, потому что он также выполняет проверку авторизации (проверьте, разрешено ли пользователю читать информацию из активного каталога). Имя пользователя и пароль могут быть действительными, но пользователю не разрешено читать информацию - и возникает исключение. Другими словами, у вас могут быть действующие имя пользователя и пароль, но при этом будет исключение.
На самом деле я запрашиваю родные эквивалент PrincipleContext, который существует только в .NET 3.5. Но если вы используете .NET 3.5 или новее, вам следует использовать PrincipleContext.
Полное решение .Net - использовать классы из пространства имен System.DirectoryServices. Они позволяют напрямую запрашивать сервер AD. Вот небольшой пример, который сделает это:
using (DirectoryEntry entry = new DirectoryEntry())
{
entry.Username = "here goes the username you want to validate";
entry.Password = "here goes the password";
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "(objectclass=user)";
try
{
searcher.FindOne();
}
catch (COMException ex)
{
if (ex.ErrorCode == -2147023570)
{
// Login or password is incorrect
}
}
}
// FindOne() didn't throw, the credentials are correct
Этот код напрямую подключается к серверу AD, используя предоставленные учетные данные. Если учетные данные недействительны, searchcher.FindOne () выдаст исключение. Код ошибки соответствует ошибке COM «неверное имя пользователя / пароль».
Вам не нужно запускать код от имени пользователя AD. Фактически, я успешно использую его для запроса информации на сервере AD от клиента за пределами домена!
как насчет типов аутентификации? Я думаю, вы забыли это в своем коде выше. :-) по умолчанию для DirectoryEntry.AuthenticationType установлено значение «Защищено», верно? этот код не будет работать с незащищенными LDAP (возможно, Anonymous или None). я правильно с этим?
Обратной стороной запрос сервера AD является то, что у вас есть разрешение для запроса сервера AD. Ваши учетные данные могут быть действительными, но если у вас нет разрешения на запрос AD, вы получите сообщение об ошибке. Вот почему был создан так называемый Быстрая привязка; вы проверяете учетные данные, не разрешая пользователю что-либо делать.
не позволит ли это кому-либо пройти, если по какой-либо другой причине будет выброшено исключение COMException до проверки учетных данных?
К сожалению, не существует "простого" способа проверить учетные данные пользователей в AD.
С каждым из представленных до сих пор методов вы можете получить ложноотрицательный результат: кредиты пользователя будут действительными, однако AD при определенных обстоятельствах вернет false:
ActiveDirectory не позволит вам использовать LDAP, чтобы определить, является ли пароль недействительным из-за того, что пользователь должен изменить пароль или его пароль истек.
Чтобы определить изменение пароля или срок действия пароля, вы можете вызвать Win32: LogonUser () и проверить код ошибки Windows на наличие следующих двух констант:
Могу я спросить, где у вас devinitions для Expired и Must_Change ... Нашел их нигде, кроме как здесь :)
Из статьи MSDN: msdn.microsoft.com/en-us/library/windows/desktop/…
Спасибо. Я пытался выяснить, почему моя проверка все время возвращала false. Это произошло потому, что пользователю нужно изменить свой пароль.
Если вы работаете с .NET 3.5 или новее, вы можете использовать пространство имен System.DirectoryServices.AccountManagement и легко проверить свои учетные данные:
// create a "principal context" - e.g. your domain (could be machine, too)
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "YOURDOMAIN"))
{
// validate the credentials
bool isValid = pc.ValidateCredentials("myuser", "mypassword");
}
Это просто, это надежно, с вашей стороны это 100% управляемый код C# - чего еще вы желаете? :-)
Прочтите все об этом здесь:
Обновлять:
Как указано в этот другой вопрос SO (и его ответы), существует проблема с этим вызовом, который может возвращать True для старых паролей пользователя. Просто помните об этом поведении и не удивляйтесь, если это произойдет :-) (спасибо @MikeGledhill за указание на это!)
Я пробовал: PrincipalContext pc = new PrincipalContext (ContextType.Domain, "yourdomain"); Ваш пробег может отличаться.
Кристиан: Вы абсолютно правы. У меня не было под рукой сервера, чтобы проверить мои предположения - и они были неправильными. Вам нужно простое доменное имя, а не дескриптор домена LDAP. Спасибо что подметил это!
Это отлично сработало, только для меня мне пришлось добавить к имени пользователя имя домена, то есть pc.ValidateCredentials ("DOMAN \\ UserName", "Password");
@myotherme: да, получите UserPrincipal, а затем используйте user.GetAuthorizationGroups(); для получения этого списка. Посмотрите отличную статью MSDN по этой теме: msdn.microsoft.com/en-us/magazine/cc135979.aspx
В моем домене мне пришлось указать pc.ValidateCredentials («myuser», «mypassword», ContextOptions.Negotiate), иначе я бы получил System.DirectoryServices.Protocols.DirectoryOperationExcepti on: Сервер не может обрабатывать запросы каталога.
Если срок действия пароля истек или учетные записи отключены, ValidateCredentials вернет false. К сожалению, он не сообщает вам, что Почему вернул false (что очень жаль, поскольку это означает, что я не могу сделать что-то разумное, например, перенаправить пользователя для изменения его пароля).
Также будьте осторожны с учетной записью «Гость» - если учетная запись гостя на уровне домена включена, ValidateCredentials вернет true, если вы предоставите ей пользователя несуществующий. В результате вы можете вызвать в UserPrinciple.FindByIdentity, чтобы узнать, существует ли переданный идентификатор пользователя первым.
Мы сделали это в нашем домене разработки / тестирования (тот, который случайным образом запускается с ошибками для тестирования различных сценариев). Если бы не это, его бы не поймали, оставив зияющую дыру в безопасности. Проблема в том, что пока у нас есть контроль над нашим собственным доменом, у нас нет никакого контроля над доменом нашего клиента, поэтому я бы предпочел иметь пуленепробиваемый код, чем делать предположения :-) Жалко, что это поведение не было задокументировано в MSDN как потребовался возраст, чтобы выследить ...
@AlexPeck: причина, по которой вам пришлось это сделать (как и мне), заключалась в том, что .NET по умолчанию использует следующие технологии: LDAP + SSL, Kerberos, затем RPC. Я подозреваю, что RPC отключен в вашей сети (хорошо!), А Kerberos фактически не используется .NET, если вы явно не укажете это с помощью ContextOptions.Negotiate.
Я также хочу добавить, что мне нужно было настроить ContextOptions.Negotiate, чтобы исправить проблему с производительностью на нашем производственном сервере. Проверка учетных данных без указания этого перечисления занимала 20–30 секунд.
@Sarah - довольно безопасно предположить, что пароль будет передан в метод, содержащий этот код, каким-либо процессом (ввод пользователя и т. д.) - показанный код является всего лишь примером, но вам нужно в какой-то момент передать пароль где-то!
@Kisame: используйте UserPrincipal.Current
не текущий пользователь, я вхожу в Windows с определенным именем пользователя, и я хочу войти в приложение с другим именем пользователя
Остерегайтесь - если вы используете это для проверки и у вас есть политика блокировки AD (например, три неправильные попытки), она заблокирует вас.
Я получаю сообщение об ошибке: Произошла локальная ошибка. Трассировка стека: в System.DirectoryServices.Protocols.LdapConnection.BindHelper (NetworkCredential newCredential, логическое значение needSetCredential) в System.DirectoryServices.AccountManagement.CredentialValidat или. AccountManagement.CredentialValidat или.BindLdap (кредитные данные NetworkC, ContextOptions contextOptions) в System.DirectoryServices.AccountManagement.Crede
@marc_s, есть ли способ обрабатывать исключения с помощью этого кода, например incorrect username/pwd, «учетная запись отключена» и т. д.? Спасибо
@Sam: ну, если возвращаемое значение - false - значит, что-то не так (учетные данные недействительны). Я не думаю, что вы можете получить больше информации, чем эта ... (также вы все равно не должны показывать больше пользователю!)
Если вы получаете это DirectoryOperationException, вместо того, чтобы сразу переходить к Negotiate, как указано в комментариях выше, по крайней мере попробуйте Negotiate | Подписание | Сначала запечатывание. Вы можете подумать, что это то, что он делал по умолчанию без каких-либо параметров (на основе документации в конструкторе PrincipalContext), но если вы используете ILSpy, вы увидите, что по умолчанию он пытается использовать Simple | SecureSocketLayer. И с этим типом исключения он просто терпит неудачу и больше ничего не пытается. Попробуйте с переговорами | Подписание | Запечатывание, и если это сработает, вы все еще в безопасности. Вот что случилось / сработало для меня.
Имейте в виду, что если пользователь ИЗМЕНИЛ свой пароль Active Directory, этот фрагмент кода продолжит успешно аутентифицировать пользователя, используя его старый пароль AD. Ага, правда. Прочтите здесь: stackoverflow.com/questions/8949501/…
Как я могу получить коды ошибок, такие как stackoverflow.com/a/11033489/206730, с помощью PrincipalContext.ValidateCredentials?
@Kiquenet: вы не можете - ValidateUser будет возвращать только bool (true / false) - не более того
Не подходит для secure LDAP connection (aka LDAPS, which uses port 636)?
@BrettVeenstra Зачем нужно использовать ContextOptions.Negotiate? Я получаю ошибку The server cannot handle directory requests, используя свой TestAd.aspx. На том же сервере: на веб-сайте возникает ошибка. На другом сайте все работает правильно. Подтвердите оба те же полномочия.
@nzpcmad Вы должны установить политику тайм-аута AD на временную блокировку. Это хорошая практика безопасности для предотвращения взлома учетной записи методом грубой силы.
@ChrisJ Это хорошая практика безопасности, поскольку вы не хотите предоставлять информацию, если кто-то угадывал учетные записи. Если они получат неправильное имя пользователя или пароль, не сообщайте им, какой из них и срок действия пароля истек. Соблюдайте только допустимые учетные данные. Это защищает вас от сбора имен учетных записей, например, что может быть использовано для попытки взлома через другие векторы.
@Shiv - но знать, почему что-то не удалось, хорошо и с точки зрения ведения журнала. Я мог бы сказать пользователю «Неверные учетные данные», но могу зарегистрировать на стороне сервера, что «Не удалось войти в систему Боб: истек пароль», а не просто «Не удалось войти в систему Боба».
@ChrisJ: ну, вы могли бы довольно легко проверить существование имени пользователя и самостоятельно проверить включенный статус. Тогда это будет означать, что проблема была в пароле. Для этого метода имеет смысл не раскрывать информацию об имени пользователя / pw, поскольку очень часто разработчики просто передают сообщение об ошибке. Это поощряет лучшие практики, поскольку используется по умолчанию.
Если вы застряли с .NET 2.0 и управляемым кодом, вот еще один способ, который работает с локальными и доменными учетными записями:
using System;
using System.Collections.Generic;
using System.Text;
using System.Security;
using System.Diagnostics;
static public bool Validate(string domain, string username, string password)
{
try
{
Process proc = new Process();
proc.StartInfo = new ProcessStartInfo()
{
FileName = "no_matter.xyz",
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
LoadUserProfile = true,
Domain = String.IsNullOrEmpty(domain) ? "" : domain,
UserName = username,
Password = Credentials.ToSecureString(password)
};
proc.Start();
proc.WaitForExit();
}
catch (System.ComponentModel.Win32Exception ex)
{
switch (ex.NativeErrorCode)
{
case 1326: return false;
case 2: return true;
default: throw ex;
}
}
catch (Exception ex)
{
throw ex;
}
return false;
}
Хорошо работает с локальными учетными записями машины, на которой запускается скрипт
Кстати, этот метод необходим, чтобы сделать это общедоступным static SecureString ToSecureString (строка PwString) {char [] PasswordChars = PwString.ToCharArray (); Пароль SecureString = новый SecureString (); foreach (символ c в PasswordChars) Password.AppendChar (c); ProcessStartInfo foo = новый ProcessStartInfo (); foo.Password = Пароль; return foo.Password; }
Напротив, в любом случае следует использовать SecureString для паролей. WPF PasswordBox поддерживает это.
Еще один вызов .NET для быстрой аутентификации учетных данных LDAP:
using System.DirectoryServices;
using(var DE = new DirectoryEntry(path, username, password)
{
try
{
DE.RefreshCache(); // This will force credentials validation
}
catch (COMException ex)
{
// Validation failed - handle how you want
}
}
Это единственное решение, которое сработало для меня, использование PrincipalContext у меня не сработало.
PrincipalContext недействителен для безопасного соединения LDAP (он же LDAPS, который использует порт 636).
В нескольких представленных здесь решениях отсутствует возможность отличить неправильный пользователь / пароль от пароля, который необходимо изменить. Это можно сделать следующим образом:
using System;
using System.DirectoryServices.Protocols;
using System.Net;
namespace ProtocolTest
{
class Program
{
static void Main(string[] args)
{
try
{
LdapConnection connection = new LdapConnection("ldap.fabrikam.com");
NetworkCredential credential = new NetworkCredential("user", "password");
connection.Credential = credential;
connection.Bind();
Console.WriteLine("logged in");
}
catch (LdapException lexc)
{
String error = lexc.ServerErrorMessage;
Console.WriteLine(lexc);
}
catch (Exception exc)
{
Console.WriteLine(exc);
}
}
}
}
Если пароль пользователя неверен или пользователь не существует, ошибка будет содержать
"8009030C: LdapErr: DSID-0C0904DC, комментарий: ошибка AcceptSecurityContext, данные 52e, v1db1",
если необходимо изменить пароль пользователя, он будет содержать
«8009030C: LdapErr: DSID-0C0904DC, комментарий: ошибка AcceptSecurityContext, данные 773, v1db1»
Значение данных lexc.ServerErrorMessage - это шестнадцатеричное представление кода ошибки Win32. Это те же коды ошибок, которые были бы возвращены при ином вызове Win32 LogonUser API. В приведенном ниже списке представлен диапазон общих значений с шестнадцатеричными и десятичными значениями:
525 user not found (1317)
52e invalid credentials (1326)
530 not permitted to logon at this time (1328)
531 not permitted to logon at this workstation (1329)
532 password expired (1330)
533 account disabled (1331)
701 account expired (1793)
773 user must reset password (1907)
775 user account locked (1909)
К сожалению, некоторые установки AD не возвращают дополнительный код ошибки LDAP, что означает, что это решение не будет работать.
Не забудьте добавить ссылки на проект: System.DirectoryServices и System.DirectoryServices.Protocols.
Однако у меня такой вопрос: как узнать имя сервера LDAP? Если вы пишете портативное приложение, вы не можете ожидать, что пользователь будет знать или указывать имена серверов AD в каждой сети.
У меня есть пользователи, которым разрешен вход на определенные рабочие станции; как указать рабочую станцию, для которой я пытаюсь войти в систему? (например, рабочая станция1 выйдет из строя с данными 531, рабочая станция2 будет работать нормально)
Я чувствую себя странно, потому что не думаю, что тебе достаточно заслуг. Это полностью управляемый метод без проблем с вызовом Win32 API, чтобы определить, «должен ли пользователь сбрасывать пароль», чего явно не достиг ни один из других ответов. Есть ли в этом методе лазейка, из-за которой он не ценится? Хм...
Спасибо. К сожалению, как я писал в первом комментарии, это может не сработать. Так что это может быть управляемый метод, но он не всегда работает.
Моя простая функция
private bool IsValidActiveDirectoryUser(string activeDirectoryServerDomain, string username, string password)
{
try
{
DirectoryEntry de = new DirectoryEntry("LDAP://" + activeDirectoryServerDomain, username + "@" + activeDirectoryServerDomain, password, AuthenticationTypes.Secure);
DirectorySearcher ds = new DirectorySearcher(de);
ds.FindOne();
return true;
}
catch //(Exception ex)
{
return false;
}
}
Проверка подлинности Windows может завершиться ошибкой по разным причинам: неправильное имя пользователя или пароль, заблокированная учетная запись, просроченный пароль и т. д. Чтобы различать эти ошибки, вызовите функцию API LogonUser через P / Invoke и проверьте код ошибки, если функция возвращает false:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
public static class Win32Authentication
{
private class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle() // called by P/Invoke
: base(true)
{
}
protected override bool ReleaseHandle()
{
return CloseHandle(this.handle);
}
}
private enum LogonType : uint
{
Network = 3, // LOGON32_LOGON_NETWORK
}
private enum LogonProvider : uint
{
WinNT50 = 3, // LOGON32_PROVIDER_WINNT50
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LogonUser(
string userName, string domain, string password,
LogonType logonType, LogonProvider logonProvider,
out SafeTokenHandle token);
public static void AuthenticateUser(string userName, string password)
{
string domain = null;
string[] parts = userName.Split('\\');
if (parts.Length == 2)
{
domain = parts[0];
userName = parts[1];
}
SafeTokenHandle token;
if (LogonUser(userName, domain, password, LogonType.Network, LogonProvider.WinNT50, out token))
token.Dispose();
else
throw new Win32Exception(); // calls Marshal.GetLastWin32Error()
}
}
Пример использования:
try
{
Win32Authentication.AuthenticateUser("EXAMPLE\\user", "P@ssw0rd");
// Or: Win32Authentication.AuthenticateUser("[email protected]", "P@ssw0rd");
}
catch (Win32Exception ex)
{
switch (ex.NativeErrorCode)
{
case 1326: // ERROR_LOGON_FAILURE (incorrect user name or password)
// ...
case 1327: // ERROR_ACCOUNT_RESTRICTION
// ...
case 1330: // ERROR_PASSWORD_EXPIRED
// ...
case 1331: // ERROR_ACCOUNT_DISABLED
// ...
case 1907: // ERROR_PASSWORD_MUST_CHANGE
// ...
case 1909: // ERROR_ACCOUNT_LOCKED_OUT
// ...
default: // Other
break;
}
}
Примечание: LogonUser требует доверительных отношений с доменом, в котором вы проверяете.
Вы можете объяснить, почему ваш ответ лучше, чем ответ, получивший наибольшее количество голосов?
@MohammadAli: если вам нужно узнать, почему не удалось проверить учетные данные (неверные учетные данные, заблокированная учетная запись, просроченный пароль и т. д.), Функция API LogonUser сообщит вам об этом. Напротив, метод PrincipalContext.ValidateCredentials (согласно комментариям к ответу marc_s) не работает; он возвращает false во всех этих случаях. С другой стороны, LogonUser требует доверительных отношений с доменом, а PrincipalContext.ValidateCredentials (я думаю) - нет.
Вот мое полное решение для проверки подлинности для вашей справки.
Сначала добавьте следующие четыре ссылки
using System.DirectoryServices;
using System.DirectoryServices.Protocols;
using System.DirectoryServices.AccountManagement;
using System.Net;
private void AuthUser() {
try{
string Uid = "USER_NAME";
string Pass = "PASSWORD";
if (Uid == "")
{
MessageBox.Show("Username cannot be null");
}
else if (Pass == "")
{
MessageBox.Show("Password cannot be null");
}
else
{
LdapConnection connection = new LdapConnection("YOUR DOMAIN");
NetworkCredential credential = new NetworkCredential(Uid, Pass);
connection.Credential = credential;
connection.Bind();
// after authenticate Loading user details to data table
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, Uid);
DirectoryEntry up_User = (DirectoryEntry)user.GetUnderlyingObject();
DirectorySearcher deSearch = new DirectorySearcher(up_User);
SearchResultCollection results = deSearch.FindAll();
ResultPropertyCollection rpc = results[0].Properties;
DataTable dt = new DataTable();
DataRow toInsert = dt.NewRow();
dt.Rows.InsertAt(toInsert, 0);
foreach (string rp in rpc.PropertyNames)
{
if (rpc[rp][0].ToString() != "System.Byte[]")
{
dt.Columns.Add(rp.ToString(), typeof(System.String));
foreach (DataRow row in dt.Rows)
{
row[rp.ToString()] = rpc[rp][0].ToString();
}
}
}
//You can load data to grid view and see for reference only
dataGridView1.DataSource = dt;
}
} //Error Handling part
catch (LdapException lexc)
{
String error = lexc.ServerErrorMessage;
string pp = error.Substring(76, 4);
string ppp = pp.Trim();
if ("52e" == ppp)
{
MessageBox.Show("Invalid Username or password, contact ADA Team");
}
if ("775" == ppp)
{
MessageBox.Show("User account locked, contact ADA Team");
}
if ("525" == ppp)
{
MessageBox.Show("User not found, contact ADA Team");
}
if ("530" == ppp)
{
MessageBox.Show("Not permitted to logon at this time, contact ADA Team");
}
if ("531" == ppp)
{
MessageBox.Show("Not permitted to logon at this workstation, contact ADA Team");
}
if ("532" == ppp)
{
MessageBox.Show("Password expired, contact ADA Team");
}
if ("533" == ppp)
{
MessageBox.Show("Account disabled, contact ADA Team");
}
if ("533" == ppp)
{
MessageBox.Show("Account disabled, contact ADA Team");
}
} //common error handling
catch (Exception exc)
{
MessageBox.Show("Invalid Username or password, contact ADA Team");
}
finally {
tbUID.Text = "";
tbPass.Text = "";
}
}
Для меня оба из приведенных ниже сработали, убедитесь, что ваш домен указан с LDAP: // в начале
//"LDAP://" + domainName
private void btnValidate_Click(object sender, RoutedEventArgs e)
{
try
{
DirectoryEntry de = new DirectoryEntry(txtDomainName.Text, txtUsername.Text, txtPassword.Text);
DirectorySearcher dsearch = new DirectorySearcher(de);
SearchResult results = null;
results = dsearch.FindOne();
MessageBox.Show("Validation Success.");
}
catch (LdapException ex)
{
MessageBox.Show($"Validation Failure. {ex.GetBaseException().Message}");
}
catch (Exception ex)
{
MessageBox.Show($"Validation Failure. {ex.GetBaseException().Message}");
}
}
private void btnValidate2_Click(object sender, RoutedEventArgs e)
{
try
{
LdapConnection lcon = new LdapConnection(new LdapDirectoryIdentifier((string)null, false, false));
NetworkCredential nc = new NetworkCredential(txtUsername.Text,
txtPassword.Text, txtDomainName.Text);
lcon.Credential = nc;
lcon.AuthType = AuthType.Negotiate;
lcon.Bind(nc);
MessageBox.Show("Validation Success.");
}
catch (LdapException ex)
{
MessageBox.Show($"Validation Failure. {ex.GetBaseException().Message}");
}
catch (Exception ex)
{
MessageBox.Show($"Validation Failure. {ex.GetBaseException().Message}");
}
}
Как указывает @Alan, у LogonUser API есть много полезных свойств, помимо вызова System.DirectoryServices.