Active Directory'de bir kullanıcının grupları nasıl alınır? (c #, asp.net)


109

Mevcut kullanıcının gruplarını almak için bu kodu kullanıyorum. Ama kullanıcıya manuel olarak vermek ve sonra gruplarını almak istiyorum. Bunu nasıl yapabilirim?

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

Yanıtlar:


163

.NET 3.5 veya üzerindeyseniz, bunu System.DirectoryServices.AccountManagementeskisinden çok daha kolay hale getiren yeni (S.DS.AM) ad alanını kullanabilirsiniz .

Bununla ilgili her şeyi buradan okuyun: .NET Framework 3.5'te Dizin Güvenliği İlkelerini Yönetme

Güncelleme: Eski MSDN dergisi makaleleri maalesef artık çevrimiçi değil - Microsoft'tan Ocak 2008 MSDN dergisi için CHM'yi indirmeniz ve oradaki makaleyi okumanız gerekir.

Temel olarak, bir "ana bağlam" (tipik olarak alanınız), bir kullanıcı sorumlusuna sahip olmanız ve ardından gruplarını çok kolay bir şekilde almanız gerekir:

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

ve hepsi bu kadar! Artık kullanıcının ait olduğu yetkilendirme gruplarının bir sonucuna (bir listeye) sahipsiniz - bunların üzerinde yineleyin, adlarını yazdırın veya ne yapmanız gerekiyorsa yapın.

Güncelleme:UserPrincipal Nesne üzerinde ortaya çıkmayan belirli özelliklere erişmek için, temelini araştırmanız gerekir 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;
}

Güncelleme # 2: Bu iki kod parçacığını bir araya getirmek çok zor olmamalı ... ama tamam - işte burada:

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: Ne yazık ki, bu özellik doğrudan üzerinde mevcut değil UserPrincipal- ona nasıl ulaşılacağına dair güncellenmiş cevabıma bakın.
marc_s


@Tassito: öyleyse 1) bir etki alanı bağlamı oluşturun, 2) bu kullanıcıyı adına göre bulun ve 3) departmanını almak için kod pasajımı kullanın
marc_s

1
GetGroups yöntemi benim için işe yaramadı, yeni ana bağlamı aşağıdaki gibi oluşturucunun başka bir aşırı yüklemesini kullanacak şekilde değiştirdim: PrincipalContext yourDomain = new PrincipalContext (ContextType.Domain, "192.168.2.23", "domain \ user", "password" ); her zaman aktif dizin kimlik doğrulaması ile oturum açmadığınız için tamamen mantıklıdır. Umarım yardımcı olur
Omid S.

2
Bu cevap mükemmel. Grup yinelemesini şu şekilde basitleştirmek de mümkündür: result.AddRange (user.GetAuthorizationGroups (). OfType <GroupPrincipal> ()
tlbignerd

59

GetAuthorizationGroups()iç içe geçmiş grupları bulamaz. Belirli bir kullanıcının üyesi olduğu tüm grupları (iç içe geçmiş gruplar dahil) gerçekten almak için şunu deneyin:

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

Kullandığım try/catchbazı SID'ler artık kullanılabilir çünkü çok büyük bir AD 200 gruplarının 2 out bazı istisnalar vardı çünkü. ( Translate()Çağrı bir SID -> Ad dönüşümü yapar.)


3
AD'den geçmek yerine bu teknik kullanılarak performanslar iyileştirildi. teşekkür ederim!
Philippe

GetAuthorisationGroups () benim için çok yavaş, yani 26 ve şimdiye kadar bulduğum diğer tüm kodlar Herkes, Etki Alanı Kullanıcıları vb. Gibi iyi bilinen tanımlayıcıları içermiyordu ... Sağladığınız kod tam anlamıyla anlıktır ve tüm sids'leri içerir, evet sadece yanlar ama iyi bilinen ve özel olanlar dahil ihtiyacım olan şey bu!
Thierry

19

Her şeyden önce, GetAuthorizationGroups () harika bir işlevdir ancak ne yazık ki 2 dezavantajı vardır:

  1. Performans, özellikle çok sayıda kullanıcısı ve grubu olan büyük şirketlerde zayıftır. Aslında ihtiyaç duyduğunuzdan çok daha fazla veri getirir ve sonuçtaki her döngü yinelemesi için bir sunucu çağrısı yapar
  2. Gruplar ve kullanıcılar gelişirken uygulamanızın 'bir gün' çalışmamasına neden olabilecek hatalar içerir. Microsoft sorunu fark etti ve bazı SID'lerle ilgili. Alacağınız hata "Grupları numaralandırırken bir hata oluştu"

Bu nedenle, GetAuthorizationGroups () 'ı daha iyi performans ve hata güvenliği ile değiştirmek için küçük bir işlev yazdım. Dizine alınmış alanları kullanan bir sorgu ile yalnızca 1 LDAP çağrısı yapar. Yalnızca grup adlarından ("cn" özelliği) daha fazla özelliğe ihtiyacınız varsa, kolayca genişletilebilir.

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

Harika! Thanx. Bazı kod yazmaya başladım ve GetAuthorizationGroups kullanıyordum ve tüm grupları almak için 300ms-2.5s sürdüğü için dehşete kapılmıştım. Yönteminiz 20-30 ms içinde yapılır.
Keith

4
Bu umut verici görünüyordu, ancak iç içe geçmiş grupları çözmüyor, örneğin bir kullanıcı, kendisi de x grubunun üyesi olan a grubunun üyesidir. Yukarıdaki kod sadece a grubunu gösterecek, x grubunu göstermeyecektir. Bu yöntemi tokenGroups üzerinden kullandım: stackoverflow.com/a/4460658/602449
Robert Muehsig

Robert Muehsig'in yorumuna bir göz atın - bu grupların iç içe geçtiği anlamına gelir ve daha da hızlıdır. Sadece dezavantajı sadece Dağıtım Grupları değil Güvenlik Gruplarını döndürür
Nick Rubino

@bigjim Verilerini döndürmek için yaklaşık 6 saniye sürdüğü için GetAuthorizationGroups kullanamıyorum, ancak sağladığınız kod Herkes, Etki Alanı Kullanıcıları vb. gibi iyi bilinen grupları döndürmüyor ve bunlara sahip olmam gerekiyor. Görünüşe göre, her şey sadece "özel grupları" döndürüyor ve bir kullanıcının ait olduğu her grubu döndürmüyor.
Thierry

11

AD içinde her kullanıcının bir özelliği vardır memberOf. Bu, ait olduğu tüm grupların bir listesini içerir.

İşte küçük bir kod örneği:

// (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: Evet, seni anlıyor. Yukarıdaki kod parçacığı tam olarak istediğiniz gibi yapacaktır. Son foreach döngüsünü, hata ayıklama yazdırma yerine grup adlarının bir listesini oluşturan bir döngü ile değiştirin.
Joel Etherton

2
Kullanıcının birincil grubunu (genellikle Etki Alanı Kullanıcıları) listelemede başarısız olur. Geri dönüp bu bilgileri ayrı ayrı sorgulamalısınız. GetAuthorizationGroups bu sorunu içermiyor.
Andy

1

Benim durumumda, herhangi bir açıklama olmadan GetGroups () kullanmaya devam edebilmemin tek yolu, AD'yi (Active Directory) okuma izni olan gruba kullanıcıyı (USER_WITH_PERMISSION) eklemekti. Bu kullanıcı ve parolayı geçiren PrincipalContext'in oluşturulması son derece önemlidir.

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

Çalıştırmak için Active Directory içinde izleyebileceğiniz adımlar:

  1. Active Directory'de bir grup oluşturun (veya bir tane alın) ve güvenlik sekmesi altında "Windows Yetkilendirme Erişim Grubu" ekleyin
  2. "Gelişmiş" düğmesini tıklayın
  3. "Windows Yetkilendirme Erişim Grubu" nu seçin ve "Görüntüle" yi tıklayın
  4. "TokenGroupsGlobalAndUniversal'ı oku" seçeneğini işaretleyin
  5. İstediğiniz kullanıcıyı bulun ve ilk adımdan oluşturduğunuz (aldığınız) gruba ekleyin

1
Web uygulamanızda bir hizmet / uygulama havuzu hesabı için yerleşik hesaplar kullanıyorsanız, bu muhtemelen devreye girer. Hizmet / uygulama havuzu hesabı olarak bir etki alanı hesabı kullanıyorsanız veya kod içinde bir etki alanı hesabının kimliğine bürünürseniz, varsayılan olarak okuma haklarına sahip olmalı ve bu sorunu yaşamamalıdır.
vapcguy

1

Bu benim için çalışıyor

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

Cevap, ne tür grupları almak istediğinize bağlıdır. System.DirectoryServices.AccountManagementAd iki grup alma yöntemi sağlar:

GetGroups - Geçerli sorumlunun üyesi olduğu grupları belirten bir grup nesneleri koleksiyonu döndürür.

Bu aşırı yüklenmiş yöntem yalnızca asıl kişinin doğrudan üyesi olduğu grupları döndürür; yinelemeli arama yapılmaz.

GetAuthorizationGroups - Bu kullanıcının üyesi olduğu tüm yetkilendirme gruplarını içeren temel nesnelerin bir koleksiyonunu döndürür. Bu işlev yalnızca güvenlik grupları olan grupları döndürür; dağıtım grupları döndürülmez.

Bu yöntem, tüm grupları özyinelemeli olarak arar ve kullanıcının üye olduğu grupları döndürür. İade edilen küme ayrıca, sistemin yetkilendirme amacıyla kullanıcıyı üye olarak kabul edeceği ek grupları da içerebilir.

Yani GetGroupsalır tüm kullanıcı olduğu gruplarını direkt üyesi ve GetAuthorizationGroupstüm alır yetkilendirme kullanıcının hangi gruplara doğrudan veya dolaylı üyesi.

İsimlendirilme şekillerine rağmen, biri diğerinin alt kümesi değildir. Tarafından GetGroupsdöndürülmeyen gruplar olabilir GetAuthorizationGroupsve bunun tersi de geçerlidir.

İşte bir kullanım örneği:

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

Çözümüm:

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

Çeviri'nin yerel olarak çalıştığı ancak uzaktan ei grubu çalışmadığı durumda. Çevir (typeof (NTAccount)

Uygulama kodunun LOGGED IN USER kimliğini kullanarak yürütülmesini istiyorsanız, kimliğe bürünmeyi etkinleştirin. Kimliğe bürünme, IIS aracılığıyla veya web.config dosyasına aşağıdaki öğe eklenerek etkinleştirilebilir .

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

Kimliğe bürünme etkinleştirilirse, uygulama, kullanıcı hesabınızda bulunan izinleri kullanarak yürütülür. Dolayısıyla, oturum açmış kullanıcının belirli bir ağ kaynağına erişimi varsa, ancak o zaman uygulama aracılığıyla bu kaynağa erişebilir.

Çalışkan videosundan bu bilgiler için PRAGIM teknolojisine teşekkür edin

Asp.net Bölüm 87'de Windows kimlik doğrulaması:

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

Ancak kimliğe bürünme, sunucuda çok fazla ek yük oluşturur

Belirli ağ gruplarının kullanıcılarına izin vermenin en iyi çözümü , web yapılandırmasında anonimliği reddetmektir. <authorization><deny users="?"/><authentication mode="Windows"/>

ve arkasındaki kodunuzda, tercihen global.asax'ta, HttpContext.Current.User.IsInRole kullanın :

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

NOT: Grup ters eğik çizgi ile yazılmalıdır yani "Alan Adı \ Grup"

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.