Antworten:
Wenn Sie mit .NET 3.5 oder höher arbeiten, können Sie den System.DirectoryServices.AccountManagement
Namespace verwenden und Ihre Anmeldeinformationen einfach überprüfen:
// 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");
}
Es ist einfach, es ist zuverlässig, es ist 100% C # -verwalteter Code auf Ihrer Seite - was können Sie mehr verlangen? :-)
Lesen Sie hier alles darüber:
Aktualisieren:
Wie in dieser anderen SO-Frage (und ihren Antworten) beschrieben , gibt es ein Problem mit diesem Aufruf, bei dem möglicherweise True
alte Kennwörter eines Benutzers zurückgegeben werden. Sei dir dieses Verhaltens bewusst und sei nicht zu überrascht, wenn dies passiert :-) (danke an @MikeGledhill für den Hinweis!)
UserPrinciple.FindByIdentity
um festzustellen, ob die übergebene Benutzer-ID zuerst vorhanden ist.
ContextOptions.Negotiate
.
Wir machen das in unserem Intranet
Sie müssen System.DirectoryServices verwenden.
Hier sind die Eingeweide des Codes
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
in LDAP im Klartext gespeichert wird?
Close()
eine using
Variable explizit aufzurufen .
Bei einigen hier vorgestellten Lösungen kann nicht zwischen einem falschen Benutzer / Passwort und einem Passwort unterschieden werden, das geändert werden muss. Dies kann folgendermaßen erfolgen:
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);
}
}
}
}
Wenn das Benutzerpasswort falsch ist oder der Benutzer nicht existiert, enthält der Fehler
"8009030C: LdapErr: DSID-0C0904DC, Kommentar: AcceptSecurityContext-Fehler, Daten 52e, v1db1",
Wenn das Benutzerkennwort geändert werden muss, enthält es
"8009030C: LdapErr: DSID-0C0904DC, Kommentar: AcceptSecurityContext-Fehler, Daten 773, v1db1"
Der lexc.ServerErrorMessage
Datenwert ist eine hexadezimale Darstellung des Win32-Fehlercodes. Dies sind dieselben Fehlercodes, die beim sonstigen Aufrufen des Win32 LogonUser-API-Aufrufs zurückgegeben würden. Die folgende Liste fasst eine Reihe allgemeiner Werte mit Hex- und Dezimalwerten zusammen:
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
undSystem.DirectoryServices.Protocols
sehr einfache Lösung mit 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;
}
Der NativeObject-Zugriff ist erforderlich, um einen fehlerhaften Benutzer / ein falsches Kennwort zu erkennen
PrincipleContext
- das nur in .NET 3.5 existiert. Aber wenn Sie .NET 3.5 oder neuer verwenden, sollten Sie verwendenPrincipleContext
Leider gibt es keine "einfache" Möglichkeit, die Anmeldeinformationen eines Benutzers in AD zu überprüfen.
Bei jeder bisher vorgestellten Methode wird möglicherweise ein falsch-negatives Ergebnis angezeigt: Die Creds eines Benutzers sind gültig, AD gibt jedoch unter bestimmten Umständen false zurück:
In ActiveDirectory können Sie mit LDAP nicht feststellen, ob ein Kennwort ungültig ist, weil ein Benutzer das Kennwort ändern muss oder ob sein Kennwort abgelaufen ist.
Um festzustellen, ob das Kennwort geändert wurde oder das Kennwort abgelaufen ist, können Sie Win32: LogonUser () aufrufen und den Windows-Fehlercode auf die folgenden 2 Konstanten überprüfen:
Der wahrscheinlich einfachste Weg ist PInvoke LogonUser Win32 API.eg
MSDN Referenz hier ...
Ich möchte auf jeden Fall den Anmeldetyp verwenden
LOGON32_LOGON_NETWORK (3)
Dadurch wird nur ein leichtes Token erstellt - perfekt für AuthN-Prüfungen. (Andere Typen können zum Erstellen interaktiver Sitzungen usw. verwendet werden.)
LogonUser
API muss der Benutzer das Gesetz als Teil des Betriebssystemprivilegs haben. Dies ist nicht etwas, das Benutzer erhalten - und nicht etwas, das Sie jedem Benutzer in der Organisation gewähren möchten. ( msdn.microsoft.com/en-us/library/aa378184(v=vs.85).aspx )
Eine vollständige .NET-Lösung besteht darin, die Klassen aus dem System.DirectoryServices-Namespace zu verwenden. Sie ermöglichen die direkte Abfrage eines AD-Servers. Hier ist ein kleines Beispiel, das dies tun würde:
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
Dieser Code stellt unter Verwendung der angegebenen Anmeldeinformationen eine direkte Verbindung zum AD-Server her. Wenn die Anmeldeinformationen ungültig sind, löst searcher.FindOne () eine Ausnahme aus. Der ErrorCode entspricht dem COM-Fehler "Ungültiger Benutzername / Passwort".
Sie müssen den Code nicht als AD-Benutzer ausführen. Tatsächlich verwende ich es erfolgreich, um Informationen auf einem AD-Server von einem Client außerhalb der Domäne abzufragen!
Noch ein .NET-Aufruf zur schnellen Authentifizierung von LDAP-Anmeldeinformationen:
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
}
}
Versuchen Sie diesen Code (HINWEIS: Berichten zufolge funktioniert er unter Windows Server 2000 nicht)
#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
außer Sie müssen Ihre eigene benutzerdefinierte Ausnahme für "LogonException" erstellen
Wenn Sie mit .NET 2.0 und verwaltetem Code nicht weiterkommen, können Sie auf folgende Weise mit lokalen und Domänenkonten arbeiten:
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;
}
Die Windows-Authentifizierung kann aus verschiedenen Gründen fehlschlagen: einem falschen Benutzernamen oder Kennwort, einem gesperrten Konto, einem abgelaufenen Kennwort und mehr. Um zwischen diesen Fehlern zu unterscheiden, rufen Sie die LogonUser- API-Funktion über P / Invoke auf und überprüfen Sie den Fehlercode, wenn die Funktion Folgendes zurückgibt 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()
}
}
Beispielnutzung:
try
{
Win32Authentication.AuthenticateUser("EXAMPLE\\user", "P@ssw0rd");
// Or: Win32Authentication.AuthenticateUser("user@example.com", "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;
}
}
Hinweis: Für LogonUser ist eine Vertrauensbeziehung zu der Domäne erforderlich, für die Sie eine Validierung durchführen.
Meine einfache Funktion
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;
}
}
Hier meine komplette Authentifizierungslösung als Referenz.
Fügen Sie zunächst die folgenden vier Referenzen hinzu
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 = "";
}
}