Я хочу проверить набор учетных данных на контроллере домена. например:
Username: STACKOVERFLOW\joel
Password: splotchy
Метод 1. Запрос Active Directory с олицетворением
Многие люди предлагают запросить что-нибудь в Active Directory. Если генерируется исключение, значит, вы знаете, что учетные данные недействительны - как предлагается в этом вопросе о переполнении стека .
Однако у этого подхода есть несколько серьезных недостатков :
Вы не только аутентифицируете учетную запись домена, но также выполняете неявную проверку авторизации. То есть вы читаете свойства из AD с помощью токена олицетворения. Что, если действующая учетная запись не имеет прав на чтение из AD? По умолчанию все пользователи имеют доступ для чтения, но политики домена могут быть настроены на отключение разрешений на доступ для ограниченных учетных записей (и / или групп).
Привязка к AD имеет серьезные накладные расходы, кэш схемы AD должен быть загружен на клиенте (кеш ADSI в провайдере ADSI, используемом DirectoryServices). Это потребляет ресурсы и сеть, и сервер AD, и это слишком дорого для такой простой операции, как аутентификация учетной записи пользователя.
Вы полагаетесь на ошибку исключения в неисключительном случае и предполагаете, что это означает неверное имя пользователя и пароль. Другие проблемы (например, сбой сети, сбой подключения к AD, ошибка выделения памяти и т. Д.) Затем неверно интерпретируются как сбой аутентификации.
Способ 2. LogonUser Win32 API
Другие предложили использовать LogonUser()
функцию API. Звучит неплохо, но, к сожалению, вызывающему пользователю иногда требуется разрешение, которое обычно предоставляется только самой операционной системе:
Для процесса, вызывающего LogonUser, требуется привилегия SE_TCB_NAME. Если вызывающий процесс не имеет этой привилегии, LogonUser завершается ошибкой, а GetLastError возвращает ERROR_PRIVILEGE_NOT_HELD.
В некоторых случаях для процесса, вызывающего LogonUser, также должна быть включена привилегия SE_CHANGE_NOTIFY_NAME; в противном случае LogonUser завершается ошибкой, а GetLastError возвращает ERROR_ACCESS_DENIED. Эта привилегия не требуется для локальной системной учетной записи или учетных записей, которые являются членами группы администраторов. По умолчанию SE_CHANGE_NOTIFY_NAME включен для всех пользователей, но некоторые администраторы могут отключить его для всех.
Предоставление привилегии « Действовать как часть операционной системы » - это не то, что вы хотите делать волей-неволей, как Microsoft указывает в статье базы знаний :
... процесс, вызывающий LogonUser, должен иметь привилегию SE_TCB_NAME (в диспетчере пользователей это право « Действовать как часть операционной системы »). Привилегия SE_TCB_NAME очень мощная и не должна предоставляться произвольным пользователям только для того, чтобы они могли запускать приложение, которому необходимо проверять учетные данные.
Кроме того, вызов LogonUser()
не удастся, если указан пустой пароль.
Как правильно аутентифицировать набор учетных данных домена?
Я случиться , чтобы быть звонок из управляемого кода, но это аа общий вопрос для Windows. Можно предположить, что у клиентов установлена .NET Framework 2.0.
источник
Ответы:
C # в .NET 3.5 с использованием System.DirectoryServices.AccountManagement .
bool valid = false; using (PrincipalContext context = new PrincipalContext(ContextType.Domain)) { valid = context.ValidateCredentials( username, password ); }
Это будет проверяться на текущий домен. Проверьте параметризованный конструктор PrincipalContext для других вариантов.
источник
new PrincipalContext(ContextType.Machine)
.using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Security; using System.DirectoryServices.AccountManagement; public struct Credentials { public string Username; public string Password; } public class Domain_Authentication { public Credentials Credentials; public string Domain; public Domain_Authentication(string Username, string Password, string SDomain) { Credentials.Username = Username; Credentials.Password = Password; Domain = SDomain; } public bool IsValid() { using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, Domain)) { // validate the credentials return pc.ValidateCredentials(Credentials.Username, Credentials.Password); } } }
источник
Domain
параметр при созданииPrincipalContext
, то, что мне было интересно узнать и что я нашел в этом ответе.Я использую следующий код для проверки учетных данных. Показанный ниже метод подтвердит правильность учетных данных, а если нет, то срок действия пароля истек или его необходимо изменить.
Я давно искал что-то подобное ... Надеюсь, это кому-то поможет!
using System; using System.DirectoryServices; using System.DirectoryServices.AccountManagement; using System.Runtime.InteropServices; namespace User { public static class UserValidation { [DllImport("advapi32.dll", SetLastError = true)] static extern bool LogonUser(string principal, string authority, string password, LogonTypes logonType, LogonProviders logonProvider, out IntPtr token); [DllImport("kernel32.dll", SetLastError = true)] static extern bool CloseHandle(IntPtr handle); enum LogonProviders : uint { Default = 0, // default for platform (use this!) WinNT35, // sends smoke signals to authority WinNT40, // uses NTLM WinNT50 // negotiates Kerb or NTLM } enum LogonTypes : uint { Interactive = 2, Network = 3, Batch = 4, Service = 5, Unlock = 7, NetworkCleartext = 8, NewCredentials = 9 } public const int ERROR_PASSWORD_MUST_CHANGE = 1907; public const int ERROR_LOGON_FAILURE = 1326; public const int ERROR_ACCOUNT_RESTRICTION = 1327; public const int ERROR_ACCOUNT_DISABLED = 1331; public const int ERROR_INVALID_LOGON_HOURS = 1328; public const int ERROR_NO_LOGON_SERVERS = 1311; public const int ERROR_INVALID_WORKSTATION = 1329; public const int ERROR_ACCOUNT_LOCKED_OUT = 1909; //It gives this error if the account is locked, REGARDLESS OF WHETHER VALID CREDENTIALS WERE PROVIDED!!! public const int ERROR_ACCOUNT_EXPIRED = 1793; public const int ERROR_PASSWORD_EXPIRED = 1330; public static int CheckUserLogon(string username, string password, string domain_fqdn) { int errorCode = 0; using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, domain_fqdn, "ADMIN_USER", "PASSWORD")) { if (!pc.ValidateCredentials(username, password)) { IntPtr token = new IntPtr(); try { if (!LogonUser(username, domain_fqdn, password, LogonTypes.Network, LogonProviders.Default, out token)) { errorCode = Marshal.GetLastWin32Error(); } } catch (Exception) { throw; } finally { CloseHandle(token); } } } return errorCode; } }
источник
Вот как определить локального пользователя:
public bool IsLocalUser() { return windowsIdentity.AuthenticationType == "NTLM"; }
Редактировать Иэн Бойд
Вам вообще не следует использовать NTLM. Он настолько старый и настолько плохой, что Microsoft Application Verifier (который используется для выявления распространенных ошибок программирования) выдаст предупреждение, если обнаружит, что вы используете NTLM.
Вот глава из документации Application Verifier о том, почему у них есть тест, если кто-то по ошибке использует NTLM:
источник
using System; using System.Collections.Generic; using System.Text; using System.DirectoryServices.AccountManagement; class WindowsCred { private const string SPLIT_1 = "\\"; public static bool ValidateW(string UserName, string Password) { bool valid = false; string Domain = ""; if (UserName.IndexOf("\\") != -1) { string[] arrT = UserName.Split(SPLIT_1[0]); Domain = arrT[0]; UserName = arrT[1]; } if (Domain.Length == 0) { Domain = System.Environment.MachineName; } using (PrincipalContext context = new PrincipalContext(ContextType.Domain, Domain)) { valid = context.ValidateCredentials(UserName, Password); } return valid; } }
Кашиф Муштак Оттава, Канада
источник