Як отримати групи користувачів в Active Directory? (c #, asp.net)


109

Я використовую цей код для отримання груп поточного користувача. Але я хочу вручну дати користувача, а потім отримати його групи. Як я можу це зробити?

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

Відповіді:


163

Якщо ви перебуваєте на .NET 3.5 або вище, ви можете використовувати новий System.DirectoryServices.AccountManagement(S.DS.AM) простір імен, що робить це набагато простіше, ніж раніше.

Про це читайте тут: Керування принципами безпеки каталогів у .NET Framework 3.5

Оновлення: старіші статті журналу MSDN більше не в Інтернеті, на жаль - вам потрібно буде завантажити CHM для журналу MSDN за січень 2008 року від Microsoft і прочитати статтю там.

По суті, вам потрібно мати "основний контекст" (як правило, ваш домен), головний користувач, і тоді ви дуже легко отримуєте його групи:

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

і це все є! Тепер у вас є результат (список) груп авторизації, до якого належить користувач, - повторіть їх, роздрукуйте їхні імена або все, що вам потрібно зробити.

Оновлення: для доступу до певних властивостей, які не з’являються на UserPrincipalоб’єкті, вам потрібно перекопатись до основної 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;
}

Оновлення №2: здається, не повинно бути надто складно скласти ці два фрагменти коду разом .... але нормально - ось це:

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

@Tassisto: на жаль, ця власність недоступна безпосередньо на UserPrincipal- див. Мою оновлену відповідь про те, як її отримати.
marc_s

Мені потрібно вказати ім'я користувача, щоб отримати значення поля його
відправлення

@Tassito: добре тоді 1) створити контекст домену, 2) знайти цього користувача за його ім'ям і 3) використати мій фрагмент коду, щоб отримати його відділ
marc_s

1
Метод GetGroups для мене не працював, я змінив новий головний контекст, щоб використати інше перевантаження конструктора наступним чином: PrincipalContext yourDomain = new PrincipalContext (ContextType.Domain, "192.168.2.23", "домен \ користувач", "пароль" ); це цілком логічно, оскільки ви не завжди входите через активну автентифікацію каталогу. Сподіваюся, що це допомагає
Омід С.

2
Ця відповідь відмінна. Також можна спростити ітерацію груп до: result.AddRange (user.GetAuthorizationGroups (). OfType <GroupPrincipal> ()
tlbignerd

59

GetAuthorizationGroups()не знаходить вкладених груп. Щоб дійсно отримати всі групи, якими є даний користувач (включаючи вкладені групи), спробуйте:

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

Я використовую, try/catchтому що у мене був певний виняток із 2 із 200 груп у дуже великій AD, оскільки деякі SID більше не доступні. ( Translate()Виклик здійснює перетворення SID -> імен.)


3
виступи були покращені за допомогою цієї методики замість того, щоб бігати через AD. спасибі!
Філіпп

GetAuthorisationGroups () для мене дуже повільний, тобто 26, і всі інші коди, які я знайшов до цього часу, не включають відомих ідентифікаторів, таких як Усі, Користувачі доменів тощо ... Код, який ви надали, є буквально миттєвим і включає всі сайти, так, тільки сайди, але це те, що мені потрібно, в тому числі відомі і на замовлення!
Тьєррі

19

Перш за все, GetAuthorizationGroups () - відмінна функція, але, на жаль, має 2 недоліки:

  1. Продуктивність погана, особливо у великій компанії з багатьма користувачами та групами. Він отримує набагато більше даних, ніж вам насправді потрібно, і сервер викликає кожну ітерацію циклу в результаті
  2. Він містить помилки, які можуть призвести до того, що ваша програма перестане працювати "якийсь день", коли розвиваються групи та користувачі. Microsoft визнала проблему та пов'язана з деякими SID. Ви отримаєте помилку "Помилка під час перерахунку груп"

Тому я написав невелику функцію заміни GetAuthorizationGroups () на кращу продуктивність та безпеку помилок. Він виконує лише 1 виклик LDAP із запитом із використанням індексованих полів. Його можна легко розширити, якщо вам потрібно більше властивостей, ніж лише назви груп (властивість "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;
}

Дивовижно! Дякую. Я почав писати якийсь код і використовував GetAuthorizationGroups і жахнувся, що для отримання всіх груп потрібно 300ms-2,5s. Ваш метод виконаний за 20-30 мс.
Кіт

4
Це здавалося перспективним, але воно не вирішує вкладені групи, наприклад, користувач є членом групи a, яка сама є членом групи x. Код вище буде просто показувати групу a, але не групу x. Я використовував цей метод , з допомогою tokenGroups: stackoverflow.com/a/4460658/602449
Роберт Muehsig

Погляньте на коментар Роберта Мюгсіга - це робить вкладені групи і ще швидше. Тільки внизу він повертає лише Групи безпеки, а не Групи розповсюдження
Нік Рубіно

@bigjim Не можу використовувати GetAuthorizationGroups, оскільки для повернення своїх даних потрібно майже 6 секунд, але наданий вами код не повертає відомих груп, таких як Усі, Користувачі домену тощо, і мені потрібно їх мати. Все, що там, здається, повертає лише "спеціальні групи", а не всі групи, до яких належить користувач.
Тьєррі

11

У межах AD кожен користувач має властивість memberOf. Тут міститься список усіх груп, до яких він належить.

Ось невеликий приклад коду:

// (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);
}
}

1
@Tassisto: Так, він тебе розуміє. Розріз коду вище буде виконано саме так, як вам подобається. Просто замініть остаточний цикл foreach циклом, який генерує список імен груп замість друку налагодження.
Джоель Етертон

2
Не вдасться перерахувати основну групу користувача (часто Користувачі домену). Ви повинні повернутися та запитати цю інформацію окремо. GetAuthorizationGroups не має цієї проблеми.
Енді

1

У моєму випадку єдиним способом я міг би продовжувати користуватися GetGroups () без будь-яких пояснень - додавання користувача (USER_WITH_PERMISSION) до групи, яка має дозвіл на читання AD (Active Directory). Надзвичайно важливо побудувати PrincipalContext, передаючи цього користувача та пароль.

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

Кроки, які ви можете виконати в Active Directory, щоб спрацювати:

  1. У Active Directory створіть групу (або візьміть її) і на вкладці безпеки додайте "Група доступу до авторизації Windows"
  2. Натисніть на кнопку «Додатково»
  3. Виберіть "Група доступу до авторизації Windows" та натисніть "Переглянути"
  4. Поставте прапорець "Прочитати маркерGroupsGlobalAndUniversal"
  5. Знайдіть потрібного користувача та додайте до групи, яку ви створили (взяли) з першого кроку

1
Це, ймовірно, грає, якщо ви використовуєте вбудовані акаунти для облікового запису сервісу / пулу додатків у веб-програмі. Якщо ви використовуєте обліковий запис домену в якості облікового запису служби / пулу додатків або представляєте себе в обліковому записі домену в коді, він повинен мати права читання за замовчуванням і не мати цього питання.
vapcguy

1

Це працює для мене

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

1

Відповідь залежить від того, які групи ви хочете отримати. System.DirectoryServices.AccountManagementПростір імен забезпечує два способи вилучення групи:

GetGroups - Повертає колекцію об'єктів групи, які задають групи, членом яких є поточний головний директор.

Цей перевантажений метод повертає лише ті групи, членом яких головний член є безпосередньо; не проводиться рекурсивний пошук.

GetAuthorizationGroups - Повертає колекцію основних об'єктів, яка містить усі групи авторизації, учасником яких є цей користувач. Ця функція повертає лише групи, які є групами безпеки; групи розподілу не повертаються.

Цей метод здійснює пошук усіх груп рекурсивно і повертає групи, в яких користувач входить. Повернутий набір може також включати додаткові групи, які система вважатиме користувача користувачем для цілей авторизації.

Таким чином GetGroupsотримує всі групи, користувач яких є безпосереднім членом, і GetAuthorizationGroupsотримує всі групи авторизації, користувач яких є прямим або непрямим членом.

Незважаючи на те, як вони названі, один не є підмножиною іншого. Можуть бути групи, повернуті GetGroupsне поверненими GetAuthorizationGroups, і навпаки.

Ось приклад використання:

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();

1

Моє рішення:

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

0

У випадку, якщо Translate працює локально, але не віддалено ei групі. Перекласти (typeof (NTAccount)

Якщо ви хочете, щоб код програми виконувався за допомогою ідентифікації LOGGED IN USER, тоді ввімкніть себе за іншим особою. Оформлення себе можна ввімкнути через IIS або додавши наступний елемент у web.config .

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

Якщо ввімкнено імпонітування, програма виконує використання дозволів, знайдених у вашому обліковому записі користувача. Отже, якщо користувач, що ввійшов, має доступ до певного мережевого ресурсу, лише тоді він зможе отримати доступ до цього ресурсу через додаток.

Дякуємо PRAGIM tech за цю інформацію з його старанного відео

Аутентифікація Windows на asp.net, частина 87:

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

Але видання себе створює багато накладних витрат на сервері

Найкраще рішення для дозволу користувачам певних мережевих груп - забороняти анонімність у веб-конфігурації <authorization><deny users="?"/><authentication mode="Windows"/>

а у своєму коді позаду, бажано в глобальній.asax , використовуйте 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

ПРИМІТКА. Група повинна бути написана з зворотним косою рисою \ "" Домен \ TheGroup "

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.