Bagaimana cara mendapatkan grup pengguna di Active Directory? (c #, asp.net)

109

Saya menggunakan kode ini untuk mendapatkan grup dari pengguna saat ini. Tapi saya ingin memberikan pengguna secara manual dan kemudian mendapatkan grupnya. Bagaimana saya bisa melakukan ini?

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
sumber

Jawaban:

163

Jika Anda menggunakan .NET 3.5 atau lebih tinggi, Anda dapat menggunakan System.DirectoryServices.AccountManagementnamespace (S.DS.AM) baru yang membuat ini jauh lebih mudah daripada sebelumnya.

Baca semuanya di sini: Mengelola Prinsip Keamanan Direktori di .NET Framework 3.5

Pembaruan: Sayangnya, artikel majalah MSDN lama tidak lagi online - Anda harus mengunduh CHM untuk majalah MSDN Januari 2008 dari Microsoft dan membaca artikel di sana.

Pada dasarnya, Anda perlu memiliki "konteks utama" (biasanya domain Anda), prinsip pengguna, dan kemudian Anda mendapatkan grupnya dengan sangat mudah:

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

dan hanya itu yang ada! Anda sekarang memiliki hasil (daftar) dari grup otorisasi milik pengguna - mengulanginya, mencetak nama mereka atau apa pun yang perlu Anda lakukan.

Pembaruan: Untuk mengakses properti tertentu, yang tidak muncul pada UserPrincipalobjek, Anda perlu menggali yang mendasarinya 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;
}

Pembaruan # 2: tampaknya seharusnya tidak terlalu sulit untuk menyatukan kedua cuplikan kode ini .... tapi ok - ini dia:

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
sumber
@ Tassisto: sayangnya, properti itu tidak tersedia secara langsung di UserPrincipal- lihat jawaban saya yang diperbarui untuk mengetahui cara mendapatkannya.
marc_s
Saya perlu memberikan nama pengguna untuk mendapatkan nilai departement-field
Tassisto
@Tassito: baiklah 1) buat konteks domain, 2) temukan pengguna itu dengan namanya, dan 3) gunakan cuplikan kode saya untuk mendapatkan departemennya
marc_s
1
Metode GetGroups tidak berhasil untuk saya, saya mengubah konteks utama baru untuk menggunakan kelebihan lain dari konstruktor sebagai berikut: PrincipalContext yourDomain = new PrincipalContext (ContextType.Domain, "192.168.2.23", "domain \ user", "password" ); itu sepenuhnya logis karena Anda tidak selalu masuk melalui otentikasi direktori aktif. Semoga membantu
Omid S.
2
Jawaban ini luar biasa. Ini juga memungkinkan untuk menyederhanakan iterasi grup menjadi: result.AddRange (user.GetAuthorizationGroups (). OfType <GroupPrincipal> ()
tlbignerd
59

GetAuthorizationGroups()tidak menemukan grup bertingkat. Untuk benar-benar mendapatkan semua grup tempat pengguna tertentu adalah anggotanya (termasuk grup bertingkat), coba ini:

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

Saya menggunakan try/catchkarena saya memiliki beberapa pengecualian dengan 2 dari 200 grup di AD yang sangat besar karena beberapa SID tidak lagi tersedia. ( Translate()Panggilan melakukan SID -> Konversi nama.)

Mickey Mouse
sumber
3
pertunjukan ditingkatkan dengan menggunakan teknik ini daripada berjalan melalui AD. Terima kasih!
Philippe
GetAuthorisationGroups () sangat lambat untuk saya yaitu 26 dan semua kode lain yang saya temukan sejauh ini tidak menyertakan pengenal terkenal seperti Semua Orang, Pengguna Domain, dll ... Kode yang Anda berikan bersifat instan dan mencakup semua sids, ya hanya sids tapi itulah yang saya butuhkan, termasuk yang terkenal dan custom!
Thierry
19

Pertama-tama, GetAuthorizationGroups () adalah fungsi yang hebat tetapi sayangnya memiliki 2 kelemahan:

  1. Performa buruk, terutama di perusahaan besar dengan banyak pengguna dan grup. Ini mengambil lebih banyak data daripada yang sebenarnya Anda butuhkan dan melakukan panggilan server untuk setiap iterasi loop pada hasilnya
  2. Ini berisi bug yang dapat menyebabkan aplikasi Anda berhenti bekerja 'suatu hari' ketika grup dan pengguna berkembang. Microsoft mengenali masalah tersebut dan terkait dengan beberapa SID. Kesalahan yang akan Anda dapatkan adalah "Terjadi kesalahan saat menghitung grup"

Oleh karena itu, saya telah menulis sebuah fungsi kecil untuk menggantikan GetAuthorizationGroups () dengan kinerja yang lebih baik dan aman dari kesalahan. Itu hanya melakukan 1 panggilan LDAP dengan kueri menggunakan bidang yang diindeks. Ini dapat dengan mudah diperpanjang jika Anda membutuhkan lebih banyak properti daripada hanya nama grup (properti "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;
}
Bigjim
sumber
Hebat! Thanx. Saya mulai menulis beberapa kode dan menggunakan GetAuthorizationGroups dan merasa ngeri karena butuh 300ms-2.5s untuk mendapatkan semua grup. Metode Anda selesai dalam 20-30 ms.
Keith
4
Ini tampak menjanjikan, tetapi tidak menyelesaikan grup bertingkat, misalnya pengguna adalah anggota grup a, yang juga merupakan anggota grup x. Kode di atas hanya akan menampilkan grup a, tetapi bukan grup x. Saya menggunakan metode ini melalui tokenGroups: stackoverflow.com/a/4460658/602449
Robert Muehsig
Lihatlah komentar Robert Muehsig - ini melakukan grup bertingkat dan bahkan lebih cepat. Satunya downside itu hanya mengembalikan Grup Keamanan bukan Grup Distribusi
Nick Rubino
@bigjim Tidak dapat menggunakan GetAuthorizationGroups karena memerlukan waktu hampir 6 detik untuk mengembalikan datanya, tetapi kode yang Anda berikan tidak mengembalikan grup terkenal seperti Semua Orang, Pengguna Domain, dll ... dan saya perlu memilikinya. Segala sesuatu di luar sana tampaknya hanya mengembalikan "grup kustom" dan tidak setiap grup milik pengguna.
Thierry
11

Dalam AD setiap pengguna memiliki properti memberOf. Ini berisi daftar semua grup yang dia ikuti.

Berikut adalah contoh kode kecil:

// (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
sumber
1
@ Tassisto: Ya, dia mengerti Anda. Potongan kode di atas akan berfungsi sesuai keinginan Anda. Cukup ganti loop foreach terakhir dengan loop yang menghasilkan daftar nama grup alih-alih pencetakan debug.
Joel Etherton
2
Ini akan gagal untuk membuat daftar grup utama pengguna (sering Pengguna Domain). Anda harus kembali dan menanyakan informasi itu secara terpisah. GetAuthorizationGroups tidak memiliki masalah ini.
Andy
1

Dalam kasus saya, satu-satunya cara agar saya dapat tetap menggunakan GetGroups () tanpa ekspresi apa pun adalah menambahkan pengguna (USER_WITH_PERMISSION) ke grup yang memiliki izin untuk membaca AD (Active Directory). Sangat penting untuk membangun PrincipalContext dengan meneruskan pengguna dan kata sandi ini.

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

Langkah-langkah yang dapat Anda ikuti di dalam Active Directory untuk membuatnya berfungsi:

  1. Ke Active Directory buat grup (atau ambil satu) dan di bawah tab secutiry tambahkan "Windows Authorization Access Group"
  2. Klik pada tombol "Advanced"
  3. Pilih "Grup Akses Otorisasi Windows" dan klik "Lihat"
  4. Centang "Baca tokenGroupsGlobalAndUniversal"
  5. Temukan pengguna yang diinginkan dan tambahkan ke grup yang Anda buat (diambil) dari langkah pertama
Gandarez
sumber
1
Ini kemungkinan akan berlaku jika Anda menggunakan akun bawaan untuk akun kumpulan layanan / aplikasi di aplikasi web Anda. Jika Anda menggunakan akun domain sebagai layanan / akun kumpulan aplikasi, atau meniru akun domain dalam kode, seharusnya memiliki hak baca secara default dan tidak mengalami masalah ini.
vapcguy
1

Ini berhasil untuk saya

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
sumber
1

Jawabannya tergantung pada jenis grup yang ingin Anda ambil. The System.DirectoryServices.AccountManagementnamespace menyediakan dua metode pengambilan kelompok:

GetGroups - Mengembalikan kumpulan objek grup yang menentukan grup di mana kepala sekolah saat ini adalah anggotanya.

Metode kelebihan beban ini hanya mengembalikan grup yang kepala sekolahnya adalah anggotanya secara langsung; tidak ada pencarian rekursif yang dilakukan.

GetAuthorizationGroups - Mengembalikan kumpulan objek utama yang berisi semua grup otorisasi di mana pengguna ini adalah anggotanya. Fungsi ini hanya mengembalikan grup yang merupakan grup keamanan; grup distribusi tidak dikembalikan.

Metode ini mencari semua grup secara rekursif dan mengembalikan grup di mana pengguna menjadi anggotanya. Kumpulan yang dikembalikan juga dapat menyertakan grup tambahan yang sistem akan menganggap pengguna sebagai anggota untuk tujuan otorisasi.

Jadi GetGroupsdapatkan semua grup di mana pengguna adalah anggota langsung , dan GetAuthorizationGroupsdapatkan semua grup otorisasi di mana pengguna adalah anggota langsung atau tidak langsung .

Terlepas dari cara mereka diberi nama, yang satu bukanlah bagian dari yang lain. Mungkin ada rombongan yang dikembalikan oleh GetGroupstidak dikembalikan oleh GetAuthorizationGroups, begitu pula sebaliknya.

Berikut contoh penggunaan:

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
sumber
1

Solusi saya:

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
sumber
0

Jika Terjemahan berfungsi secara lokal tetapi tidak secara remote grup.Terjemahan (typeof (NTAccount)

Jika Anda ingin kode aplikasi dijalankan menggunakan identitas LOGGED IN USER, aktifkan peniruan identitas. Peniruan identitas dapat diaktifkan melalui IIS atau dengan menambahkan elemen berikut di web.config .

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

Jika peniruan identitas diaktifkan, aplikasi dijalankan menggunakan izin yang ditemukan di akun pengguna Anda. Jadi jika pengguna yang masuk memiliki akses, ke sumber daya jaringan tertentu, hanya dengan demikian ia dapat mengakses sumber daya itu melalui aplikasi.

Terima kasih PRAGIM tech untuk informasi ini dari videonya yang rajin

Otentikasi Windows di asp.net Bagian 87:

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

Tetapi peniruan identitas menciptakan banyak biaya tambahan di server

Solusi terbaik untuk memungkinkan pengguna grup jaringan tertentu adalah menolak anonim di konfigurasi web <authorization><deny users="?"/><authentication mode="Windows"/>

dan di belakang kode Anda, sebaiknya di global.asax, gunakan 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

CATATAN: Grup harus ditulis dengan garis miring terbalik \ ie "TheDomain \ TheGroup"

Pierre-David Sabourin
sumber