Active Directory Zugriff per .NET
Geschrieben von: Christoph Wille Active Directory ist das zentrale Element einer Windows 2000 Domain Infrastruktur. Es gibt viele Zugriffsmethoden (ADSI oder LDAP, um zwei sehr bekannte zu wählen), die mehr oder minder komplex sind. Mit dem .NET Framework ist Microsoft angetreten, es leichter zu machen. Heute werden wir uns das anhand des Zugriffs als auch der Suche nach Elementen ansehen. Voraussetzung um den Sourcecode dieses Artikels verwenden zu können ist eine Installation des Microsoft .NET Framework SDK's auf einem Webserver. Weiters setze ich voraus, daß der Leser die Programmiersprache C# zu einem gewissen Grad beherrscht - es finden sich etliche Artikel auf diesem Server, um das notwendige Wissen zu erlernen. Sicherheitshinweis: die Beispielannahme ist, daß der ASP.NET Worker Prozess unter dem SYSTEM Konto läuft (standardmäßig läuft er unter dem ASPNET Account). Bitte ändern Sie processModel in machine.config entsprechend ab, oder geben nur Administratoren Zugriff auf die Dateien (Impersonation). Beispieldaten anlegenEs empfiehlt sich nicht, die ersten Gehversuche mit Echtdaten vorzunehmen. Eine Organizational Unit (OU) mit Testdaten ist da schon deutlich besser geeignet. Das Anlegen einer OU passiert in Active Directory User and Computers: Da man ja nicht immer als Domain-Administrator eingeloggt ist (hoffentlich nicht!), kann man sich ohne Ab-/Anmelden speziell für die jeweils gewünschte Applikation als Domain-Administrator ausgeben: mit Run As. Dieses nette Kontextmenü erhält man übrigens duch die Kombination Shift Taste und rechte Maustaste. Es öffnet sich folgender Dialog: Hier gibt man die entsprechenden Logindaten ein, und schon startet Active Directory User and Computers unter einem beliebigen User Account: Hier habe ich bereits eine OU angelegt ("Demo"), sowie einen User Account für unsere Experimente ("Max Mustermann"). Die Domain heißt für die nachfolgenden Beispiele Dev.AlfaSierraPapa.com, der DC den wir ansprechen Strangelove. Der Zugriff ins Active DirectoryDie gesamte Funktionalität für den Zugriff auf das Active Directory ist im System.DirectoryServices Namespace implementiert. Um mir den Import der Assembly in jeder ASP.NET Datei zu ersparen, habe ich das in der web.config vorgenommen: <configuration> <system.web> <compilation debug="true"> <assemblies> <add assembly="System.DirectoryServices, Version=1.0.3300.0,... </assemblies> </compilation> </system.web> </configuration> Beginnen wir mit dem Zugriff auf einen einzelnen Active Directory Eintrag (simplestart.aspx): <% @Page Language="C#" %> <% @Import Namespace="System.DirectoryServices" %> <% DirectoryEntry de = new DirectoryEntry("LDAP://strangelove/OU=Demo,DC=Dev,DC=AlfaSierraPapa,DC=COM", "DEV\\Administrator", "foryoureyesonly"); foreach(DirectoryEntry child in de.Children) { Response.Write(child.Name); } %> Die Klasse zum Zugriff auf ein einzelnes Verzeichnisobjekt ist DirectoryEntry. Diesem übergebe ich in obigen Beispiel den LDAP (Lightweight Directory Access Protocol) String, unter dem das Objekt zu finden ist, sowie separat Useraccountdaten. Warum der User Account separat? Nun, vertrauen in Impersonisierung ist gut, händisches sicherstellen ist deutlich angenehmer und spart Stunden der Fehlersuche. Der LDAP String sieht nur auf den ersten Blick fürchterlich aus, er ist aber völlig logisch aufgebaut, und wir werden heute noch einige Variationen sehen. Der String wird von rechts nach links immer spezifischer - von der COM Domain hin zum gewünschten OU Objekt. Der Servername muß nicht angegeben werden, fehlt er, wird er automatisch mit Hilfe von Active Directory ermittelt. Nach dem Zugriff auf das Objekt liste ich die Kindelemente auf, das sieht für die Beispiel-OU dann so aus: CN=Max Mustermann Der Common Name (CN) des einzigen Kindobjektes in dieser OU ist Max Mustermann. Erweitern wir das Beispiel ein wenig (somemore.aspx): <% @Page Language="C#" %> <% @Import Namespace="System.DirectoryServices" %> <% DirectoryEntry de = new DirectoryEntry("LDAP://strangelove/OU=Demo,DC=Dev,DC=AlfaSierraPapa,DC=COM", "DEV\\Administrator", "youwontexpectthatone"); DirectoryEntry parent = de.Parent; foreach(DirectoryEntry child in parent.Children) { Response.Write(child.Name + "<br>"); } DirectoryEntry dcOU = parent.Children.Find("OU=Domain Controllers"); foreach(DirectoryEntry child in dcOU.Children) { Response.Write(child.Name + "<br>"); } %> Mit der Parent Eigenschaft kann man auf das im Baum darüberliegende Objekt zugreifen, und dort ebenso die Objekte auflisten. Ebenfalls praktisch ist die Find Methode zum Suchen von Objekten, in diesem Falle suche ich nach der OU für Domain Controller. Der Output sieht dann so aus: CN=Builtin CN=Computers OU=Demo OU=Domain Controllers CN=ForeignSecurityPrincipals CN=Infrastructure CN=LostAndFound CN=System CN=Users CN=STRANGELOVE CN=WEBDEVSRV01 Die letzten beiden Einträge sind die Domain Controller, die in der Domain Controllers OU abgelegt sind, der Rest sind die Top-Level Objekte der Domain. Suchen im Active DirectoryDie Find Methode haben wir schon kennengelernt, jetzt werden wir uns aber mit dem richtigen Tool anfreunden: der DirectorySearcher Klasse. Zur Einstimmung gleich ein Beispiel: <% @Page Language="C#" %> <% @Import Namespace="System.DirectoryServices" %> <% DirectoryEntry de = new DirectoryEntry("LDAP://OU=Demo,DC=Dev,DC=AlfaSierraPapa,DC=COM", "DEV\\Administrator", "itisyourguess"); // Angabe Suchausdruck und zu holende properties (PropertiesToLoad) DirectorySearcher src = new DirectorySearcher("(objectClass=*)", new string[]{"sn","givenName","title"}); src.SearchRoot = de; src.SearchScope = SearchScope.OneLevel; foreach(SearchResult res in src.FindAll()) { Response.Write(res.Properties["sn"][0] + "<br>"); } %> In diesem Beispiel verwende ich beim öffnen des DirectoryEntry keine Serverangabe. Danach erstelle ich ein DirectorySearcher Objekt, dem ich den Suchausdruck und eine Liste der Eigenschaften mitgebe, die ich aus dem Active Directory zurückgeliefert bekommen möchte (pro Objekt werden sehr viele Eigenschaften gespeichert, hier geht es also um Performance). Es fehlt noch die Angabe des SearchRoots, also wo die Suche beginnen soll, und wie im Baum gesucht werden soll - das SearchScope. Die folgende Grafik illustriert die drei verschiedenen SearchScopes mit einem ausgewählten SearchRoot: Ich hatte OneLevel gewählt, also hat DirectorySearcher nur nach Elementen in der aktuellen OU gesucht. Die Suche beginnt allerdings erst, sobald man FindAll aufruft. Natürlich kann der Suchbegriff auch komplizierter werden, zB kann man nach allen Usern einer Domain suchen, wie im folgenden Beispiel gezeigt (allusers.aspx): <% @Page Language="C#" %> <% @Import Namespace="System.DirectoryServices" %> <% DirectoryEntry de = new DirectoryEntry("LDAP://DC=Dev,DC=AlfaSierraPapa,DC=COM", "DEV\\Administrator", "noway"); // Angabe Suchausdruck DirectorySearcher src = new DirectorySearcher("(&(objectCategory=Person)(objectClass=user))"); src.SearchRoot = de; src.SearchScope = SearchScope.Subtree; foreach(SearchResult res in src.FindAll()) { Response.Write(res.Properties["Name"][0] + "<br>"); } %> Hier ist eine UND-Verknüpfung eingesetzt (für eine genaue Diskussion der Abfragesprache muß ich leider an die Dokumentation verweisen), und die Suche erstreckt sich auf den gesamten Unterbaum (Subtree), ausgehend vom Domain Root. Minimale Zeilenanzahl für maximalen Erfolg. SchlußbemerkungHeute habe ich mir nur einen Unteraspekt der Active Directory Funktionen von .NET herausgepickt. Es ist noch einiges weiteres vorhanden - so zB das Anlegen von Objekten, Löschen, Verschieben, etc. Aber das ist eine andere Geschichte. Download des CodesKlicken Sie hier, um den Download zu starten. Wenn Sie jetzt Fragen haben...Wenn Sie Fragen rund um die in diesem Artikel vorgestellte Technologie haben, dann schauen Sie einfach bei uns in den Community Foren der deutschen .NET Community vorbei. Die Teilnehmer helfen Ihnen gerne, wenn Sie sich zur im Artikel vorgestellten Technologie weiterbilden möchten. Eine weitere sehr hilfreiche Resource ist das deutsche ASP.NET Wiki, das als zentrale Anlaufstelle für Tips, Tricks, Know How und alles Nützliche was man in seinem Alltag als (ASP).NET-Entwickler so braucht und entdeckt gedacht ist. Haben Sie Fragen die sich direkt auf den Inhalt des Artikels beziehen, dann schreiben Sie dem Autor! Unsere Autoren freuen sich über Feedback zu ihren Artikeln. Ein einfacher Klick auf die Autor kontaktieren Schaltfläche (weiter unten) und schon haben Sie ein für diesen Artikel personalisiertes Anfrageformular.
Und zu guter Letzt möchten wir Sie bitten, den Artikel zu bewerten. Damit helfen Sie uns, die Qualität der Artikel zu verbessern - und anderen Lesern bei der Auswahl der Artikel, die sie lesen sollten.
©2000-2006 AspHeute.com |