Làm cách nào để có được các nhóm người dùng trong Active Directory? (c #, asp.net)


109

Tôi sử dụng mã này để lấy các nhóm của người dùng hiện tại. Nhưng tôi muốn cung cấp thủ công cho người dùng và sau đó nhận các nhóm của anh ta. Tôi có thể làm cái này như thế nào?

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

Câu trả lời:


163

Nếu bạn đang sử dụng .NET 3.5 trở lên, bạn có thể sử dụng System.DirectoryServices.AccountManagementkhông gian tên (S.DS.AM) mới, điều này làm cho việc này dễ dàng hơn rất nhiều so với trước đây.

Đọc tất cả về nó ở đây: Quản lý các nguyên tắc bảo mật thư mục trong .NET Framework 3.5

Cập nhật: các bài báo trên tạp chí MSDN cũ hơn không còn trực tuyến nữa - bạn sẽ cần tải xuống CHM cho tạp chí MSDN tháng 1 năm 2008 từ Microsoft và đọc bài báo trong đó.

Về cơ bản, bạn cần có "ngữ cảnh chính" (thường là miền của bạn), người dùng chính và sau đó bạn có được các nhóm của nó rất dễ dàng:

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

và đó là tất cả! Bây giờ bạn có kết quả (danh sách) các nhóm ủy quyền mà người dùng thuộc về - lặp lại chúng, in ra tên của họ hoặc bất cứ điều gì bạn cần làm.

Cập nhật: Để truy cập các thuộc tính nhất định, không hiển thị trên UserPrincipalđối tượng, bạn cần phải tìm hiểu sâu cơ bản 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;
}

Cập nhật # 2: Có vẻ như không quá khó để ghép hai đoạn mã này lại với nhau .... nhưng được rồi - đây là:

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: rất tiếc, thuộc tính đó không có sẵn trực tiếp trên UserPrincipal- hãy xem câu trả lời cập nhật của tôi để biết cách truy cập vào nó.
marc_s

Tôi cần phải cung cấp tên người dùng để có được giá trị của Lãnh thổ hải-lĩnh vực của mình
Tassisto

@Tassito: vậy thì 1) tạo ngữ cảnh miền, 2) tìm người dùng đó theo tên của người đó và 3) sử dụng đoạn mã của tôi để nhận bộ phận của họ
marc_s

1
Phương thức GetGroups không hoạt động đối với tôi, tôi đã thay đổi ngữ cảnh chính mới để sử dụng phương thức nạp chồng khác của phương thức khởi tạo như sau: MajorContext yourDomain = new PrincipalContext (ContextType.Domain, "192.168.2.23", "domain \ user", "password" ); nó hoàn toàn hợp lý vì bạn không phải lúc nào cũng đăng nhập thông qua xác thực thư mục hoạt động. Hy vọng nó sẽ hữu ích
Omid S.

2
Câu trả lời này là tuyệt vời. Nó cũng có thể đơn giản hóa các nhóm lặp để: result.AddRange (user.GetAuthorizationGroups () OfType <GroupPrincipal> ().
tlbignerd

59

GetAuthorizationGroups()không tìm thấy các nhóm lồng nhau. Để thực sự nhận được tất cả các nhóm mà một người dùng nhất định là thành viên của (bao gồm cả các nhóm lồng nhau), hãy thử cách này:

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

Tôi sử dụng try/catchvì tôi đã có một số ngoại lệ với 2 trong số 200 nhóm trong một QUẢNG CÁO rất lớn vì một số SID không còn khả dụng. (Cuộc Translate()gọi thực hiện một SID -> Chuyển đổi tên.)


3
màn trình diễn đã được cải thiện bằng cách sử dụng kỹ thuật này thay vì chạy qua AD. cảm ơn bạn!
Philippe

GetAuthorisationGroups () siêu chậm đối với tôi, tức là 26 và tất cả các mã khác mà tôi tìm thấy cho đến nay không bao gồm các số nhận dạng nổi tiếng như Mọi người, Người dùng miền, v.v. Mã bạn cung cấp là ngay lập tức và bao gồm tất cả các mã, vâng chỉ có những cái nắp nhưng đó là thứ tôi cần, bao gồm những cái nổi tiếng và tùy chỉnh!
Thierry

19

Trước hết, GetAuthorizationGroups () là một hàm tuyệt vời nhưng không may có 2 nhược điểm:

  1. Hiệu suất kém, đặc biệt là ở các công ty lớn với nhiều người dùng và nhóm. Nó tìm nạp nhiều dữ liệu hơn khi đó bạn thực sự cần và thực hiện lệnh gọi máy chủ cho mỗi lần lặp vòng lặp trong kết quả
  2. Nó chứa các lỗi có thể khiến ứng dụng của bạn ngừng hoạt động 'một ngày nào đó' khi các nhóm và người dùng đang phát triển. Microsoft đã nhận ra sự cố và có liên quan đến một số SID. Lỗi bạn sẽ nhận được là "Đã xảy ra lỗi khi liệt kê các nhóm"

Do đó, tôi đã viết một hàm nhỏ để thay thế GetAuthorizationGroups () với hiệu suất tốt hơn và an toàn với lỗi. Nó chỉ thực hiện 1 cuộc gọi LDAP với một truy vấn sử dụng các trường được lập chỉ mục. Nó có thể dễ dàng mở rộng nếu bạn cần nhiều thuộc tính hơn chỉ có tên nhóm (thuộc tính "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;
}

Tuyệt vời! Thanx. Tôi bắt đầu viết một số mã và đang sử dụng GetAuthorizationGroups và thật kinh ngạc rằng phải mất 300ms-2,5s để có được tất cả các nhóm. Phương pháp của bạn được thực hiện trong 20-30 ms.
Keith

4
Điều này có vẻ đầy hứa hẹn, nhưng nó không giải quyết được các nhóm lồng nhau, ví dụ như một người dùng là thành viên của nhóm a, chính là thành viên của nhóm x. Đoạn mã trên sẽ chỉ hiển thị nhóm a, nhưng không hiển thị nhóm x. Tôi đã sử dụng phương pháp này qua tokenGroups: stackoverflow.com/a/4460658/602449
Robert Muehsig

Hãy xem nhận xét của Robert Muehsig - điều này thực hiện các nhóm lồng nhau và thậm chí còn nhanh hơn. Chỉ có nhược điểm là nó chỉ trả về Nhóm bảo mật không phải Nhóm phân phối
Nick Rubino

@bigjim Không thể sử dụng GetAuthorizationGroups vì mất gần 6 giây để trả về dữ liệu của nó nhưng mã mà bạn cung cấp không trả về các nhóm nổi tiếng như Mọi người, Người dùng miền, v.v. và tôi cần phải có những thứ này. Mọi thứ ngoài kia dường như chỉ trả về "nhóm tùy chỉnh" và không phải mọi nhóm mà người dùng thuộc về.
Thierry

11

Trong AD mỗi người dùng có một tài sản memberOf. Điều này chứa danh sách tất cả các nhóm mà anh ấy thuộc về.

Đây là một ví dụ mã nhỏ:

// (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: Vâng, anh ấy hiểu bạn. Đoạn mã trên sẽ làm chính xác như bạn muốn. Chỉ cần thay thế vòng lặp foreach cuối cùng bằng một vòng lặp tạo danh sách tên nhóm thay vì in gỡ lỗi.
Joel Etherton

2
Nó sẽ không liệt kê được nhóm chính của người dùng (thường là Người dùng miền). Bạn phải quay lại và truy vấn thông tin đó một cách riêng biệt. GetAuthorizationGroups không có vấn đề này.
Andy

1

Trong trường hợp của tôi, cách duy nhất để tôi có thể tiếp tục sử dụng GetGroups () mà không cần bất kỳ sự cố nào là thêm người dùng (USER_WITH_PERMISSION) vào nhóm có quyền đọc QUẢNG CÁO (Active Directory). Điều cực kỳ cần thiết là phải xây dựng ChiefContext chuyển người dùng và mật khẩu này.

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

Các bước bạn có thể làm theo bên trong Active Directory để nó hoạt động:

  1. Trong Active Directory, tạo một nhóm (hoặc lấy một nhóm) và trong tab secutiry thêm "Nhóm truy cập ủy quyền của Windows"
  2. Nhấp vào nút "Nâng cao"
  3. Chọn "Nhóm truy cập ủy quyền của Windows" và nhấp vào "Xem"
  4. Kiểm tra "Đọc mã thông báoGroupsGlobalAndUniversal"
  5. Định vị người dùng mong muốn và thêm vào nhóm bạn đã tạo (thực hiện) từ bước đầu tiên

1
Điều này có thể phát huy tác dụng nếu bạn sử dụng tài khoản tích hợp cho tài khoản nhóm dịch vụ / ứng dụng trong ứng dụng web của mình. Nếu bạn sử dụng tài khoản miền làm tài khoản nhóm dịch vụ / ứng dụng hoặc mạo danh tài khoản miền trong mã, tài khoản đó phải có quyền đọc theo mặc định và không gặp sự cố này.
vapcguy

1

Điều này phù hợp với tôi

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

Câu trả lời phụ thuộc vào loại nhóm bạn muốn truy xuất. Không System.DirectoryServices.AccountManagementgian tên cung cấp hai phương pháp truy xuất nhóm:

GetGroups - Trả về một tập hợp các đối tượng nhóm chỉ định các nhóm mà hiệu trưởng hiện tại là thành viên.

Phương thức nạp chồng này chỉ trả về các nhóm mà người chính trực tiếp là thành viên; không có tìm kiếm đệ quy nào được thực hiện.

GetAuthorizationGroups - Trả về một tập hợp các đối tượng chính chứa tất cả các nhóm ủy quyền mà người dùng này là thành viên. Hàm này chỉ trả về các nhóm là nhóm bảo mật; nhóm phân phối không được trả lại.

Phương thức này tìm kiếm tất cả các nhóm một cách đệ quy và trả về các nhóm mà người dùng là thành viên. Tập hợp được trả lại cũng có thể bao gồm các nhóm bổ sung mà hệ thống sẽ coi người dùng là thành viên cho các mục đích ủy quyền.

Vì vậy, GetGroupsnhận tất cả các nhóm mà người dùng là thành viên trực tiếpGetAuthorizationGroupsnhận tất cả các nhóm ủy quyền mà người dùng là thành viên trực tiếp hoặc gián tiếp .

Bất chấp cách chúng được đặt tên, một cái không phải là một tập hợp con của cái kia. Có thể có nhóm được trả lại bởi GetGroupskhông được trả lại bởi GetAuthorizationGroupsvà ngược lại.

Đây là một ví dụ sử dụng:

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

Giải pháp của tôi:

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

Trong trường hợp Dịch hoạt động cục bộ nhưng không hoạt động từ xa nhóm ei. Dịch (typeof (NTAccount)

Nếu bạn muốn mã ứng dụng thực thi bằng cách sử dụng danh tính NGƯỜI DÙNG ĐĂNG NHẬP, thì hãy bật tính năng mạo danh. Mạo danh có thể được kích hoạt thông qua IIS hoặc bằng cách thêm phần tử sau vào web.config .

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

Nếu tính năng mạo danh được bật, ứng dụng sẽ thực thi bằng các quyền có trong tài khoản người dùng của bạn. Vì vậy, nếu người dùng đã đăng nhập có quyền truy cập vào một tài nguyên mạng cụ thể, thì chỉ khi đó anh ta mới có thể truy cập tài nguyên đó thông qua ứng dụng.

Cảm ơn công nghệ PRAGIM về thông tin này từ video siêng năng của anh ấy

Xác thực Windows trong asp.net Phần 87:

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

Nhưng việc mạo danh tạo ra nhiều chi phí trên máy chủ

Giải pháp tốt nhất để cho phép người dùng của một số nhóm mạng nhất định là từ chối ẩn danh trong cấu hình web <authorization><deny users="?"/><authentication mode="Windows"/>

và trong mã của bạn phía sau, tốt nhất là trong global.asax, hãy sử dụng 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

LƯU Ý: Nhóm phải được viết bằng dấu gạch chéo ngược \ tức là "TheDomain \ TheGroup"

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.