Как я могу проверить имя пользователя и пароль в Active Directory? Я просто хочу проверить правильность имени пользователя и пароля.
Проверить имя пользователя и пароль в Active Directory?
Ответы (14)
Если вы работаете с .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 # - чего еще вы желаете? :-)
Прочтите все об этом здесь:
- Управление принципами безопасности каталогов в .NET Framework 3.5
- Документы MSDN в System.DirectoryServices.AccountManagement
Обновление:
Как указано в этом другом вопросе SO (и его ответах), там - проблема с этим вызовом, который может возвращать True
для старых паролей пользователя. Просто помните об этом поведении и не удивляйтесь, если это произойдет :-) (спасибо @MikeGledhill за указание на это!)
user.GetAuthorizationGroups();
, чтобы получить этот список. Ознакомьтесь с замечательной статьей MSDN по этой теме: msdn.microsoft.com/en- us / magazine / cc135979.aspx
- person marc_s; 12.04.2010
UserPrinciple.FindByIdentity
, чтобы увидеть, существует ли переданный идентификатор пользователя первым.
- person Chris J; 08.09.2011
ContextOptions.Negotiate
.
- person Brett Veenstra; 21.09.2011
ContextOptions.Negotiate
, чтобы исправить проблему с производительностью на нашем производственном сервере. Проверка учетных данных без указания этого перечисления занимала 20–30 секунд.
- person Justin Helgerson; 02.04.2012
UserPrincipal.Current
- person marc_s; 17.12.2012
incorrect username/pwd
, 'аккаунт отключен' и т. д.? Спасибо
- person smr5; 20.01.2015
false
- значит что-то не так (учетные данные недействительны). Я не думаю, что вы можете получить больше информации, чем эта ... (также вы в любом случае не должны показывать больше пользователю!)
- person marc_s; 20.01.2015
PrincipalContext.ValidateCredentials
?
- person Kiquenet; 17.02.2016
ValidateUser
будет возвращать только bool (true / false) - не более того
- person marc_s; 17.02.2016
secure LDAP connection (aka LDAPS, which uses port 636)
?
- person Kiquenet; 19.12.2016
The server cannot handle directory requests
ошибку при использовании моего TestAd.aspx. На том же сервере: на веб-сайте возникает ошибка. На другом сайте все работает правильно. Подтвердите одинаковые учетные данные на обоих.
- person Kiquenet; 23.12.2016
Мы делаем это в нашем Интранете
Вы должны использовать 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();
}
}
}
strPassword
хранится в LDAP в виде обычного текста?
- person Matt Kocaj; 17.07.2015
Close()
для using
переменной.
- person Nyerguds; 17.05.2016
В нескольких представленных здесь решениях отсутствует возможность отличить неправильный пользователь / пароль от пароля, который необходимо изменить. Это можно сделать следующим образом:
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)
System.DirectoryServices
и System.DirectoryServices.Protocols
- person TomXP411; 05.04.2013
очень простое решение с использованием 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
- person Ian Boyd; 18.08.2011
К сожалению, не существует "простого" способа проверить учетные данные пользователей в AD.
С каждым из представленных до сих пор методов вы можете получить ложноотрицательный результат: кредиты пользователя будут действительными, однако AD при определенных обстоятельствах вернет false:
- Пользователь должен изменить пароль при следующем входе в систему.
- Срок действия пароля пользователя истек.
ActiveDirectory не позволит вам использовать LDAP, чтобы определить, является ли пароль недействительным из-за того, что пользователь должен изменить пароль или его пароль истек.
Чтобы определить изменение пароля или срок действия пароля, вы можете вызвать Win32: LogonUser () и проверить код ошибки Windows на наличие следующих двух констант:
- ERROR_PASSWORD_MUST_CHANGE = 1907.
- ERROR_PASSWORD_EXPIRED = 1330
Вероятно, самый простой способ - это PInvoke LogonUser Win32 API. Например.
Ссылка MSDN здесь ...
Определенно хочу использовать тип входа
LOGON32_LOGON_NETWORK (3)
Это создает только легкий токен - идеально подходит для проверок AuthN. (другие типы могут использоваться для создания интерактивных сеансов и т. д.)
LogonUser
API требует, чтобы у пользователя был уровень Действовать как часть операционной системы; это не то, что получают пользователи, и не то, что вы хотите предоставлять каждому пользователю в организации. (msdn.microsoft.com/en-us/ библиотека / aa378184 (v = vs.85) .aspx)
- person Ian Boyd; 18.08.2011
Полное решение .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 от клиента вне домена!
Еще один вызов .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
}
}
Попробуйте этот код (ПРИМЕЧАНИЕ: Сообщается, что не работает на 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»
Проверка подлинности Windows может завершиться ошибкой по разным причинам: неправильное имя пользователя или пароль, заблокированная учетная запись, просроченный пароль и т. Д. Чтобы различать эти ошибки, вызовите 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 требует доверительных отношений с доменом, в котором вы проверяете.
Если вы застряли с .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;
}
Моя простая функция
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;
}
}
Для меня оба из приведенных ниже сработали, убедитесь, что ваш домен указан с 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}");
}
}
Вот мое полное решение для проверки подлинности для вашей справки.
Сначала добавьте следующие четыре ссылки
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 = "";
}
}