[C#/.NET] Verschiedene Authentifizierungsmöglichkeiten #1 – Plain
[C#/.NET] Verschiedene Authentifizierungsmöglichkeiten #2 – Verschlüsselung und SecureString
[C#/.NET] Verschiedene Authentifizierungsmöglichkeiten #3 – MySQL und Hash

ActiveDir_auth
Bei Active Directory (ab Windows Server 2008 Active Directory Domain Services) handelt es sich um einen Verzeichnisdienst (englisch directory service) von Microsoft. Sie speichert Benutzerinformationen(Benutzernamen, Kennwort sowie Gruppen) zentral auf einer Datenbank und stellt diese den Clients im Netzwerk zur Verfügung. Dadurch ist es, im Gegensatz zu einer Arbeitsgruppe, möglich die Benutzerdaten auf jeden Rechner in der Domäne zu verteilen. Somit ist es nicht mehr nötig jedes Profil auf jedem Rechner einzurichten, was eine enorme Zeitersparnis ergibt.

In vielen Firmen wird Active Directory genutzt und eine Authentifizierung ist  mit C# in Verbindung mit dem .NET-Framework schnell geschrieben.
Ich werden hier beschreiben, wie sich ein Benutzer durch Eingabe seines Benutzernamens und Kennworts authentifizieren kann, sowie die Überprüfung, ob sich der Benutzer in einer Gruppe befindet.

Authentifizierung über Benutzernamen und Kennwort:

Zuerst widmen wir uns der Authentifizierung an einer Domäne mittels Benutzername und Passwort.
Mit folgendem Code beziehen wir die aktuell verwendete Domäne aus dem System.

System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName;

Die einfachste Variante ergibt sich mit der Verwendung von PrincipalContext-Klasse, welche sich im Namespace System.DirectoryServices.AccountManagement befindet

public static bool? AuthenticateUserWithAD(string Domain, string Username, string Password)
{
	try
	{
		using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, Domain))
		{
			//Überprüft die übergebenen Anmeldedaten mit dem angegeben ContextType (ApplicationDirectory, Domain oder Machine)
			return pc.ValidateCredentials(Username, Password); //Gibt true oder false zurück
		}
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message, ex.Source);
		return null;
	}
}

Wenn man nicht auf PrincipalContext zurückgreifen möchte, so kann man den Benutzer auch mittels DirectoryEntry aus dem Namespace System.DirectoryServices authentifizieren.

Dazu verwendet man folgenden Methode:

private static readonly int ERROR_DS_NO_SUCH_OBJECT = -2147016656; //Fehlercode für LDAP_NO_SUCH_OBJECT 0x80072030 | http://msdn.microsoft.com/en-us/library/aa746528(v=vs.85).aspx
public static DirectoryEntry GetDirectoryEntry(string Domain, string username, string password)
{
	DirectoryEntry dirEntry = new DirectoryEntry();
	dirEntry.Path = "LDAP://" + Domain;
	dirEntry.Username = username;
	dirEntry.Password = password;

	try
	{
		if (dirEntry.NativeObject == null)
		{
			// Kein ActiveDir-Objekt gefunden
			dirEntry = null;
		}
	}
	catch (COMException ex)
	{
		if (ex.ErrorCode == ERROR_DS_NO_SUCH_OBJECT)
		{
			MessageBox.Show("Es wurde kein Objekt, welches mit den Eingaben übereinstimmt gefunden", "Fehler");
		}
		else
		{
			MessageBox.Show(ex.ErrorCode.ToString() + "\r\n" + ex.Message);
		}
		dirEntry.Close();
		dirEntry = null;
	}
	return dirEntry;
}

Um nun einen Wahrheitswert (Boolean) zurückzugeben, muss man prüfen, ob das DirectoryEntry-Objekt gefüllt oder null ist.
Dazu verwendet man folgende Methode, welche wir später dann auch über unsere Klasse aufrufen.

public static bool AuthenticateUser(string Domain, string username, string password)
{
	return GetDirectoryEntry(Domain, username, password) != null;
}

Falls man nur wissen möchte, ob der Benutzer existiert, so kann man ebenfalls PrincipalContext verwenden, jedoch diesmal in Verbindung mit der statischen Methode FindByIdentity der UserPrincipal-Klasse

public static bool CheckIfUserExists(string Domain, string Username)
{
	using (var pc = new PrincipalContext(ContextType.Domain, Domain))
	{
		using (var User = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, Username))
		{
			return User != null;
		}
	}
}

Überprüfung, ob sich Benutzer in Gruppe befindet:

Nun möchte man natürlich nicht immer den Benutzer authentifizieren oder prüfen, ob dieser existiert, sondern spezielle Funktionen einem gewissen Kreis von Personen bereitstellen.

Dazu bietet ActiveDirectory s.g. Gruppen

Eine Gruppe besteht aus Benutzer- und Computerkonten, Kontakten und anderen Gruppen, die als eine Einheit verwaltet werden können. Benutzer und Computer, die zu einer bestimmten Gruppe gehören, werden als Gruppenmitglieder bezeichnet.
Die Verwendung von Gruppen vereinfacht die Verwaltung, indem vielen Konten in einem Schritt einheitliche Berechtigungen und Rechte zugewiesen werden können, anstatt jedem Konto einzeln Berechtigungen und Rechte zuweisen zu müssen.

Für die Prüfung, ob der Benutzer Mitglied der Gruppe ist, gibt es zwei Möglichkeiten.

  1. Unter Verwendung von DirectoryEntry
  2. Benutzung WindowsIdentity

Zuerst unter Verwendung von DirectoryEntry

public static bool? CheckIfUserIsInGroupWithDirectoryEntry(string Domain, string Username, string Password, string Group)
{
	if (Username == "" || Password == "")
	{
		return false;
	}

	try
	{
		DirectoryEntry entry = new DirectoryEntry("LDAP://" + Domain, Username, Password);
		DirectorySearcher mySearcher = new DirectorySearcher(entry);
		mySearcher.Filter = "(&(objectClass=user)(|(cn=" + Username + ")(sAMAccountName=" + Username + ")))";
		SearchResult result = mySearcher.FindOne();

		foreach (string GroupPath in result.Properties["memberOf"])
		{
			if (GroupPath.Contains(Group))
			{
				return true;
			}
		}
	}
	catch (DirectoryServicesCOMException DirSvrComEx)
	{
		MessageBox.Show(DirSvrComEx.Message);
		return null;
	}
	return false;
}

Und die Alternative mit WindowsIdentity:

public static bool? CheckIfUserIsInGroup(string Username, string Groupname)
{
	try
	{
		using (var identity = new WindowsIdentity(Username))
		{
			var principal = new WindowsPrincipal(identity);
			return principal.IsInRole(Groupname);
		}
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message);
		return null;
	}
}

Möchte man den aktuellen Benutzer verwenden (und somit keine Eingabe der Login-Informationen erfordern), so kann man die Methode wie folgt anpassen

public static bool? CheckIfCurrentUserIsInGroup(string Groupname)
{
	try
	{
		IntPtr accountToken = WindowsIdentity.GetCurrent().Token;
		using (var identity = new WindowsIdentity(accountToken))
		{
			var principal = new WindowsPrincipal(identity);
			return principal.IsInRole(Groupname);
		}
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message);
		return null;
	}
}

Zu guter Letzt das Demo-Projekt:

20130811_Verweis-Manager - OleDBTest_000034

ActiveDirectory_Auth.zip