Wie erhalte ich die Gruppen eines Benutzers in Active Directory? (c #, asp.net)

108

Ich benutze diesen Code, um die Gruppen des aktuellen Benutzers zu erhalten. Aber ich möchte dem Benutzer manuell geben und dann seine Gruppen erhalten. Wie kann ich das machen?

using System.Security.Principal;

public ArrayList Groups()
{
    ArrayList groups = new ArrayList();

    foreach (IdentityReference group in System.Web.HttpContext.Current.Request.LogonUserIdentity.Groups)
    {
        groups.Add(group.Translate(typeof(NTAccount)).ToString());
    }

    return groups;
}
Tassisto
quelle

Antworten:

162

Wenn Sie mit .NET 3.5 oder höher arbeiten, können Sie das neue verwenden System.DirectoryServices.AccountManagement höher arbeiten Namespace (S.DS.AM) verwenden, was dies viel einfacher macht als früher.

Lesen Sie hier alles darüber: Verwalten von Verzeichnissicherheitsprinzipalen in .NET Framework 3.5

Update: Ältere Artikel des MSDN-Magazins sind leider nicht mehr online - das müssen Sie das CHM für das MSDN-Magazin vom Januar 2008 von Microsoft herunterladen und den Artikel dort lesen.

Grundsätzlich benötigen Sie einen "Hauptkontext" (normalerweise Ihre Domain), einen Benutzerprinzipal, und dann erhalten Sie seine Gruppen sehr einfach:

public List<GroupPrincipal> GetGroups(string userName)
{
   List<GroupPrincipal> result = new List<GroupPrincipal>();

   // establish domain context
   PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain);

   // find your user
   UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, userName);

   // if found - grab its groups
   if(user != null)
   {
      PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups();

      // iterate over all groups
      foreach(Principal p in groups)
      {
         // make sure to add only group principals
         if(p is GroupPrincipal)
         {
             result.Add((GroupPrincipal)p);
         }
      }
   }

   return result;
}

und das ist alles was es gibt! Sie haben jetzt ein Ergebnis (eine Liste) von Berechtigungsgruppen, zu denen der Benutzer gehört - iterieren Sie über sie, drucken Sie ihre Namen aus oder was auch immer Sie tun müssen.

Update: Um auf bestimmte Eigenschaften zuzugreifen, die auf dem UserPrincipalObjekt nicht angezeigt werden, müssen Sie sich mit dem zugrunde liegenden Objekt befassen DirectoryEntry:

public string GetDepartment(Principal principal)
{
    string result = string.Empty;

    DirectoryEntry de = (principal.GetUnderlyingObject() as DirectoryEntry);

    if (de != null)
    {
       if (de.Properties.Contains("department"))
       {
          result = de.Properties["department"][0].ToString();
       }
    }

    return result;
}

Update Nr. 2: Es sollte nicht allzu schwierig sein, diese beiden Codeausschnitte zusammenzufügen ... aber ok - hier ist es:

public string GetDepartment(string username)
{
    string result = string.Empty;

    // if you do repeated domain access, you might want to do this *once* outside this method, 
    // and pass it in as a second parameter!
    PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain);

    // find the user
    UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, username);

    // if user is found
    if(user != null)
    {
       // get DirectoryEntry underlying it
       DirectoryEntry de = (user.GetUnderlyingObject() as DirectoryEntry);

       if (de != null)
       {
          if (de.Properties.Contains("department"))
          {
             result = de.Properties["department"][0].ToString();
          }
       }
    }

    return result;
}
marc_s
quelle
@Tassisto: Leider ist diese Eigenschaft nicht direkt auf der UserPrincipal- siehe meine aktualisierte Antwort, wie man darauf kommt.
marc_s
Ich muss den Benutzernamen
angeben
@Tassito: Nun, dann 1) erstelle einen Domain-Kontext, 2) finde diesen Benutzer anhand seines Namens und 3) benutze mein Code-Snippet, um seine Abteilung zu erhalten
marc_s
1
Die GetGroups-Methode hat bei mir nicht funktioniert. Ich habe den neuen Hauptkontext geändert, um eine weitere Überladung des Konstruktors wie folgt zu verwenden: PrincipalContext yourDomain = neuer PrincipalContext (ContextType.Domain, "192.168.2.23", "domain \ user", "password" ); Dies ist völlig logisch, da Sie nicht immer über die Active Directory-Authentifizierung angemeldet sind. Hoffe es hilft
Omid S.
2
Diese Antwort ist ausgezeichnet. Es ist auch möglich, die Gruppeniteration zu vereinfachen: result.AddRange (user.GetAuthorizationGroups (). OfType <GroupPrincipal> ()
tlbignerd
59

GetAuthorizationGroups()findet keine verschachtelten Gruppen. Versuchen Sie Folgendes, um wirklich alle Gruppen zu erhalten, zu denen ein bestimmter Benutzer gehört (einschließlich verschachtelter Gruppen):

using System.Security.Principal

private List<string> GetGroups(string userName)
{
    List<string> result = new List<string>();
    WindowsIdentity wi = new WindowsIdentity(userName);

    foreach (IdentityReference group in wi.Groups)
    {
        try
        {
            result.Add(group.Translate(typeof(NTAccount)).ToString());
        }
        catch (Exception ex) { }
    }
    result.Sort();
    return result;
}

Ich benutze, try/catchweil ich einige Ausnahmen mit 2 von 200 Gruppen in einer sehr großen AD hatte, weil einige SIDs nicht mehr verfügbar waren. (Der Translate()Aufruf führt eine SID -> Namenskonvertierung durch.)

Mickey Maus
quelle
3
Die Leistung wurde durch Verwendung dieser Technik verbessert, anstatt durch AD zu laufen. Danke!
Philippe
GetAuthorisationGroups () ist für mich sehr langsam, dh 26, und alle anderen Codes, die ich bisher gefunden habe, enthielten keine bekannten Bezeichner wie Jeder, Domänenbenutzer usw. Der von Ihnen bereitgestellte Code ist literarisch augenblicklich und enthält alle Seiten. Ja, nur die Seiten, aber das ist es, was ich brauche, einschließlich der bekannten und benutzerdefinierten!
Thierry
19

Erstens ist GetAuthorizationGroups () eine großartige Funktion, hat aber leider zwei Nachteile:

  1. Die Leistung ist schlecht, insbesondere in großen Unternehmen mit vielen Benutzern und Gruppen. Es ruft viel mehr Daten ab, als Sie tatsächlich benötigen, und führt für jede Schleifeniteration im Ergebnis einen Serveraufruf durch
  2. Es enthält Fehler, die dazu führen können, dass Ihre Anwendung eines Tages nicht mehr funktioniert, wenn sich Gruppen und Benutzer weiterentwickeln. Microsoft hat das Problem erkannt und ist mit einigen SIDs verwandt. Der Fehler, den Sie erhalten, lautet "Beim Auflisten der Gruppen ist ein Fehler aufgetreten".

Daher habe ich eine kleine Funktion geschrieben, um GetAuthorizationGroups () durch bessere Leistung und Fehlersicherheit zu ersetzen. Es wird nur 1 LDAP-Aufruf mit einer Abfrage unter Verwendung indizierter Felder ausgeführt. Es kann leicht erweitert werden, wenn Sie mehr Eigenschaften als nur die Gruppennamen benötigen ("cn" -Eigenschaft).

// Usage: GetAdGroupsForUser2("domain\user") or GetAdGroupsForUser2("user","domain")
public static List<string> GetAdGroupsForUser2(string userName, string domainName = null)
{
    var result = new List<string>();

    if (userName.Contains('\\') || userName.Contains('/'))
    {
        domainName = userName.Split(new char[] { '\\', '/' })[0];
        userName = userName.Split(new char[] { '\\', '/' })[1];
    }

    using (PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, domainName))
        using (UserPrincipal user = UserPrincipal.FindByIdentity(domainContext, userName))
            using (var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domainContext.Name)))
            {
                searcher.Filter = String.Format("(&(objectCategory=group)(member={0}))", user.DistinguishedName);
                searcher.SearchScope = SearchScope.Subtree;
                searcher.PropertiesToLoad.Add("cn");

                foreach (SearchResult entry in searcher.FindAll())
                    if (entry.Properties.Contains("cn"))
                        result.Add(entry.Properties["cn"][0].ToString());
            }

    return result;
}
Bigjim
quelle
Genial! Danke. Ich fing an, Code zu schreiben und verwendete GetAuthorizationGroups und war entsetzt, dass es 300 ms bis 2,5 Sekunden dauerte, um alle Gruppen zu erhalten. Ihre Methode ist in 20-30 ms fertig.
Keith
4
Dies schien vielversprechend, löst jedoch keine verschachtelten Gruppen auf, z. B. ist ein Benutzer Mitglied der Gruppe a, die selbst Mitglied der Gruppe x ist. Der obige Code zeigt nur Gruppe a, aber nicht Gruppe x. Ich habe diese Methode über tokenGroups verwendet: stackoverflow.com/a/4460658/602449
Robert Muehsig
Schauen Sie sich den Kommentar von Robert Muehsig an - dies führt zu verschachtelten Gruppen und ist sogar noch schneller. Einziger Nachteil ist, dass nur Sicherheitsgruppen und keine Verteilergruppen zurückgegeben werden
Nick Rubino
@bigjim GetAuthorizationGroups kann nicht verwendet werden, da es fast 6 Sekunden dauert, bis die Daten zurückgegeben werden. Der von Ihnen bereitgestellte Code gibt jedoch keine bekannten Gruppen wie Jeder, Domänenbenutzer usw. zurück, und ich muss diese haben. Alles da draußen scheint nur "benutzerdefinierte Gruppen" zurückzugeben und nicht jede Gruppe, zu der ein Benutzer gehört.
Thierry
11

Innerhalb des AD hat jeder Benutzer eine Eigenschaft memberOf. Diese enthält eine Liste aller Gruppen, denen er angehört.

Hier ist ein kleines Codebeispiel:

// (replace "part_of_user_name" with some partial user name existing in your AD)
var userNameContains = "part_of_user_name";

var identity = WindowsIdentity.GetCurrent().User;
var allDomains = Forest.GetCurrentForest().Domains.Cast<Domain>();

var allSearcher = allDomains.Select(domain =>
{
    var searcher = new DirectorySearcher(new DirectoryEntry("LDAP://" + domain.Name));

    // Apply some filter to focus on only some specfic objects
    searcher.Filter = String.Format("(&(&(objectCategory=person)(objectClass=user)(name=*{0}*)))", userNameContains);
    return searcher;
});

var directoryEntriesFound = allSearcher
    .SelectMany(searcher => searcher.FindAll()
        .Cast<SearchResult>()
        .Select(result => result.GetDirectoryEntry()));

var memberOf = directoryEntriesFound.Select(entry =>
{
    using (entry)
    {
        return new
        {
            Name = entry.Name,
            GroupName = ((object[])entry.Properties["MemberOf"].Value).Select(obj => obj.ToString())
        };
    }
});

foreach (var item in memberOf)
{
    Debug.Print("Name = " + item.Name);
    Debug.Print("Member of:");

    foreach (var groupName in item.GroupName)
    {
        Debug.Print("   " + groupName);
    }

    Debug.Print(String.Empty);
}
}
Oliver
quelle
1
@Assisto: Ja, er versteht dich. Das obige Code-Snippet macht genau das, was Sie wollen. Ersetzen Sie einfach die letzte foreach-Schleife durch eine Schleife, die eine Liste der Gruppennamen generiert, anstatt den Druck zu debuggen.
Joel Etherton
2
Die primäre Gruppe des Benutzers (häufig Domänenbenutzer) wird nicht aufgelistet. Sie müssen zurückgehen und diese Informationen separat abfragen. GetAuthorizationGroups hat dieses Problem nicht.
Andy
1

In meinem Fall konnte ich GetGroups () nur dann ohne Erwartung verwenden, wenn ich den Benutzer (USER_WITH_PERMISSION) zu der Gruppe hinzufügte, die zum Lesen des AD (Active Directory) berechtigt ist. Es ist äußerst wichtig, den PrincipalContext zu erstellen, der diesen Benutzer und dieses Kennwort übergibt.

var pc = new PrincipalContext(ContextType.Domain, domain, "USER_WITH_PERMISSION", "PASS");
var user = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, userName);
var groups = user.GetGroups();

Schritte, die Sie in Active Directory ausführen können, damit es funktioniert:

  1. Erstellen Sie in Active Directory eine Gruppe (oder nehmen Sie eine) und fügen Sie auf der Registerkarte "Sicherheit" die Option "Windows-Autorisierungszugriffsgruppe" hinzu.
  2. Klicken Sie auf "Erweitert"
  3. Wählen Sie "Windows Authorization Access Group" und klicken Sie auf "View".
  4. Aktivieren Sie "Read tokenGroupsGlobalAndUniversal".
  5. Suchen Sie den gewünschten Benutzer und fügen Sie ihn der Gruppe hinzu, die Sie im ersten Schritt erstellt (übernommen) haben
Gandarez
quelle
1
Dies kommt wahrscheinlich ins Spiel, wenn Sie in Ihrer Webanwendung integrierte Konten für ein Dienst- / App-Pool-Konto verwenden. Wenn Sie ein Domänenkonto als Dienst- / App-Pool-Konto verwenden oder sich als Domänenkonto im Code ausgeben, sollte es standardmäßig über Leserechte verfügen und dieses Problem nicht aufweisen.
Vapcguy
1

Das funktioniert bei mir

public string[] GetGroupNames(string domainName, string userName)
    {
        List<string> result = new List<string>();

        using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, domainName))
        {
            using (PrincipalSearchResult<Principal> src = UserPrincipal.FindByIdentity(principalContext, userName).GetGroups())
            {
                src.ToList().ForEach(sr => result.Add(sr.SamAccountName));
            }
        }

        return result.ToArray();
    }
Taran
quelle
1

Die Antwort hängt davon ab, welche Art von Gruppen Sie abrufen möchten. Der System.DirectoryServices.AccountManagementNamespace bietet zwei Methoden zum Abrufen von Gruppen:

GetGroups - Gibt eine Sammlung von Gruppenobjekten zurück, die die Gruppen angeben, zu denen der aktuelle Principal gehört.

Diese überladene Methode gibt nur die Gruppen zurück, zu denen der Principal direkt gehört. Es werden keine rekursiven Suchen durchgeführt.

GetAuthorizationGroups - Gibt eine Sammlung von Hauptobjekten zurück, die alle Berechtigungsgruppen enthält, zu denen dieser Benutzer gehört. Diese Funktion gibt nur Gruppen zurück, die Sicherheitsgruppen sind. Verteilergruppen werden nicht zurückgegeben.

Diese Methode durchsucht alle Gruppen rekursiv und gibt die Gruppen zurück, in denen der Benutzer Mitglied ist. Der zurückgegebene Satz kann auch zusätzliche Gruppen enthalten, bei denen das System den Benutzer zu Autorisierungszwecken als Mitglied betrachtet.

So GetGroupsbekommt alle Gruppen, die der Benutzer ist ein direktes Mitglied und GetAuthorizationGroupserhält alle Berechtigungsgruppen , von denen die Nutzer sind direkt oder indirekt Mitglied.

Trotz der Art und Weise, wie sie benannt werden, ist eine keine Teilmenge der anderen. Es kann Gruppen geben, die von GetGroupsnicht zurückgegeben werden GetAuthorizationGroups, und umgekehrt.

Hier ist ein Anwendungsbeispiel:

PrincipalContext domainContext = new PrincipalContext(ContextType.Domain, "MyDomain", "OU=AllUsers,DC=MyDomain,DC=Local");
UserPrincipal inputUser = new UserPrincipal(domainContext);
inputUser.SamAccountName = "bsmith";
PrincipalSearcher adSearcher = new PrincipalSearcher(inputUser);
inputUser = (UserPrincipal)adSearcher.FindAll().ElementAt(0);
var userGroups = inputUser.GetGroups();
Tawab Wakil
quelle
1

Meine Lösung:

UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain, myDomain), IdentityType.SamAccountName, myUser);
List<string> UserADGroups = new List<string>();            
foreach (GroupPrincipal group in user.GetGroups())
{
    UserADGroups.Add(group.ToString());
}
Darcu
quelle
0

Falls Translate lokal funktioniert, aber nicht remote ei group. Übersetzen (typeof (NTAccount)

Wenn Sie möchten, dass der Anwendungscode mit der Identität LOGGED IN USER ausgeführt wird, aktivieren Sie den Identitätswechsel. Der Identitätswechsel kann über IIS oder durch Hinzufügen des folgenden Elements in der web.config aktiviert werden .

<system.web>
<identity impersonate="true"/>

Wenn der Identitätswechsel aktiviert ist, wird die Anwendung mit den in Ihrem Benutzerkonto enthaltenen Berechtigungen ausgeführt. Wenn der angemeldete Benutzer Zugriff auf eine bestimmte Netzwerkressource hat, kann er nur dann über die Anwendung auf diese Ressource zugreifen.

Vielen Dank an PRAGIM Tech für diese Informationen aus seinem sorgfältigen Video

Windows-Authentifizierung in asp.net Teil 87:

https://www.youtube.com/watch?v=zftmaZ3ySMc

Der Identitätswechsel verursacht jedoch viel Overhead auf dem Server

Die beste Lösung, um Benutzern bestimmter Netzwerkgruppen zu erlauben, besteht darin, anonym in der Webkonfiguration zu verweigern <authorization><deny users="?"/><authentication mode="Windows"/>

Verwenden Sie in Ihrem Code dahinter, vorzugsweise in der Datei global.asax, die Datei HttpContext.Current.User.IsInRole :

Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
If HttpContext.Current.User.IsInRole("TheDomain\TheGroup") Then
//code to do when user is in group
End If

HINWEIS: Die Gruppe muss mit einem Backslash geschrieben werden, dh "TheDomain \ TheGroup".

Pierre-David Sabourin
quelle