Jak uzyskać grupy użytkowników w Active Directory? (c#, asp.net)

Używam tego kodu, aby uzyskać grupy bieżącego użytkownika. Ale chcę ręcznie dać użytkownikowi, a następnie uzyskać jego grupy. Jak mogę to zrobić?

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;
}
Author: Micha Wiedenmann, 2011-03-15

7 answers

Jeśli korzystasz z. NET 3.5 lub nowszego, możesz użyć nowego System.DirectoryServices.AccountManagement (S.DS.AM) przestrzeni nazw, co sprawia, że jest to o wiele łatwiejsze niż kiedyś.

Przeczytaj o tym tutaj: Zarządzanie zabezpieczeniami katalogów w. NET Framework 3.5

Aktualizacja: starsze artykuły z MSDN magazine nie są już dostępne w Internecie, niestety-musisz pobrać CHM dla magazynu MSDN ze stycznia 2008 r. od Microsoftu i przeczytać artykuł w tam.

Zasadniczo, musisz mieć "główny kontekst" (zazwyczaj domeny), główny użytkownik, a następnie bardzo łatwo dostajesz jego grupy:

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;
}
I to wszystko! Masz teraz wynik (listę) grup autoryzacji, do których należy użytkownik - iteruj nad nimi, wydrukuj ich nazwy lub cokolwiek musisz zrobić.

Update: Aby uzyskać dostęp do pewnych właściwości, które nie znajdują się na obiekcie UserPrincipal, musisz zagłębić się w podstawowe 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 # 2: wydaje się, że nie powinno być zbyt trudno połączyć te dwa fragmenty kodu razem.... ale ok-zaczyna się:

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;
}
 140
Author: marc_s,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-28 13:39:37

GetAuthorizationGroups() nie znajduje zagnieżdżonych grup. Aby naprawdę uzyskać wszystkie grupy, których dany użytkownik jest członkiem (w tym grupy zagnieżdżone), spróbuj tego:

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;
}

Używam try/catch ponieważ miałem pewne wyjątki z 2 na 200 grup w bardzo dużej reklamie, ponieważ niektóre Sid nie były już dostępne. (Wywołanie Translate() wykonuje konwersję Sid -> nazwy.)

 50
Author: Mickey Mouse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-24 08:18:52

Po pierwsze, GetAuthorizationGroups() jest świetną funkcją, ale niestety ma 2 wady:

    Wydajność jest słaba, szczególnie w dużych firmach z wieloma użytkownikami i grupami. Pobiera o wiele więcej danych niż faktycznie potrzebujesz i wykonuje wywołanie serwera dla każdej iteracji pętli w wyniku
  1. zawiera błędy, które mogą spowodować, że aplikacja przestanie działać "pewnego dnia", gdy grupy i użytkownicy będą ewoluować. Microsoft rozpoznał problem i jest związany z niektórymi SID. Błąd, który otrzymasz, to "wystąpił błąd podczas wyliczania grup"

Dlatego napisałem małą funkcję, która zastąpi GetAuthorizationGroups () lepszą wydajnością i zabezpieczeniem przed błędami. Wykonuje tylko 1 wywołanie LDAP z zapytaniem za pomocą indeksowanych pól. Można go łatwo rozszerzyć, jeśli potrzebujesz więcej właściwości niż tylko nazwy grup (właściwość" cn").

// 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;
}
 14
Author: Bigjim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-07 06:50:13

W reklamie każdy użytkownik ma właściwość memberOf. To zawiera listę wszystkich grup, do których należy.

Oto mały przykład kodu:

// (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);
}
}
 10
Author: Oliver,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-15 10:16:22

W moim przypadku jedynym sposobem, w jaki mogłem używać GetGroups() bez żadnego expcetion, było dodanie użytkownika (USER_WITH_PERMISSION) do grupy, która ma uprawnienia do odczytu reklamy (Active Directory). Jest to niezwykle istotne, aby skonstruować PrincipalContext przekazując tego użytkownika i hasło.

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

Kroki, które możesz wykonać wewnątrz usługi Active Directory, aby ją uruchomić:

  1. do Active Directory Utwórz grupę (lub weź jedną) i w zakładce secutiry dodaj " autoryzacja systemu Windows Grupa Dostępu "
  2. Kliknij przycisk "Zaawansowane"
  3. Wybierz "Windows Authorization Access Group" i kliknij "View"
  4. Sprawdź "Czytaj tokenGroupsGlobalAndUniversal"
  5. Znajdź żądanego użytkownika i dodaj do grupy, którą utworzyłeś (wzięłaś) od pierwszego kroku
 1
Author: Gandarez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-28 11:25:06

W przypadku Translate działa lokalnie, ale nie zdalnie E.i grupy.Translate(typeof (NTAccount)

Jeśli chcesz, aby kod aplikacji był wykonywany przy użyciu tożsamości zalogowanego użytkownika, włącz podszywanie się. Podszywanie się może być włączone przez IIS lub przez dodanie następującego elementu w sieci.config .

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

Jeśli podszywanie się jest włączone, aplikacja wykonuje przy użyciu uprawnień znalezionych na koncie użytkownika. Jeśli więc zalogowany użytkownik ma dostęp do konkretnego zasobu sieciowego, tylko wtedy będzie on w stanie uzyskać dostęp do tego zasobu za pośrednictwem aplikacji.

Dziękuję PRAGIM tech za informacje z jego starannego wideo

Uwierzytelnianie Windows w asp.net część 87:

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

Ale podszywanie się tworzy dużo napowietrznych na serwerze

Najlepszym rozwiązaniem pozwalającym użytkownikom pewnych grup sieci jest odmowa anonimowości w konfiguracji sieci <authorization><deny users="?"/><authentication mode="Windows"/>

I w Twoim kodzie za, najlepiej w globalnym.asax, użyj HttpContext.Aktualne.Użytkownik.Isinrol :

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

Uwaga: Grupa musi być zapisana z odwrotnym ukośnikiem \ tj. "TheDomain\Thebroup"

 0
Author: Pierre-David Sabourin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-14 17:59:07

To działa dla mnie

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();
    }
 0
Author: Taran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-19 16:44:09