【问题标题】:How to determine all the groups a user belongs to (including nested groups) in ActiveDirectory and .NET 3.5如何在 ActiveDirectory 和 .NET 3.5 中确定用户所属的所有组(包括嵌套组)
【发布时间】:2011-07-15 19:50:02
【问题描述】:

我有一个使用 ActiveDirecotry 授权的应用程序,并且已决定它需要支持嵌套的 AD 组,例如:

MAIN_AD_GROUP
     |
     |-> SUB_GROUP
              | 
              |-> User

因此,该用户直接MAIN_AD_GROUP 的成员。我希望能够递归查找用户,搜索嵌套在MAIN_AD_GROUP 中的组。

主要问题是我使用的是 .NET 3.5,而 .NET 3.5 中的 System.DirectoryServices.AccountManagement 存在一个错误,因此方法 UserPrincipal.IsMemberOf() 不适用于 超过 1500 个用户的组。所以我不能使用UserPrincipal.IsMemberOf(),不,我也不能切换到.NET 4。

我已经使用以下函数解决了最后一个问题:

private bool IsMember(Principal userPrincipal, Principal groupPrincipal)
{
    using (var groups = userPrincipal.GetGroups())
    {
        var isMember = groups.Any(g => 
            g.DistinguishedName == groupPrincipal.DistinguishedName);
        return isMember;
    }
}

userPrincipal.GetGroups() 只返回用户是其直接成员的组。

如何让它与嵌套组一起使用?

【问题讨论】:

    标签: c# .net-3.5 active-directory directoryservices


    【解决方案1】:

    我知道这是一个旧线程,但它是 Google 上的最高结果,所以如果这对任何人都有帮助,这就是我想出的使用 AccountManagement 的东西,但使这个特定的查询更容易。

    public static class AccountManagementExtensions
    {
        public static bool IsNestedMemberOf(this Principal principal, GroupPrincipal group)
        {
            // LDAP Query for memberOf Nested 
            var filter = String.Format("(&(sAMAccountName={0})(memberOf:1.2.840.113556.1.4.1941:={1}))",
                    principal.SamAccountName,
                    group.DistinguishedName
                );
    
            var searcher = new DirectorySearcher(filter);
    
            var result = searcher.FindOne();
    
            return result != null;
        }
    }
    

    【讨论】:

      【解决方案2】:

      有效的方法是通过使用正确的 DirectorySearcher 过滤器来执行单个 AD 查询,例如

      public bool CheckMemberShip(string userName)
          {
      
              bool membership = false;
              string connection = "LDAP://"+YOURDOMAIN;
              DirectoryEntry entry = new DirectoryEntry(connection);
              DirectorySearcher mySearcher = new DirectorySearcher(entry);
              mySearcher.Filter = "(&(objectClass=user)(memberOf:1.2.840.113556.1.4.1941:=cn=GROUPNAME,OU=Groups,OU=ABC,OU=ABC,OU=IND,DC=ad,DC=COMPANY,DC=com)(|(sAMAccountName=" + userName + ")))";
              SearchResult result = mySearcher.FindOne();
      
              // No search result, hence no membership
              if (result == null)
              {
                  membership = false;
              }
      
              entry.Close();
              entry.Dispose();
              mySearcher.Dispose();
      
              membership = true;
              return membership;
          }
      

      您需要将 YOURDOMAIN 和 GROUPNAME 替换为 AD 中的正确值。

      来源:How to Recursively Get the Group Membership of a User in Active Directory using .NET/C# and LDAP (without just 2 hits to Active Directory)

      需要包含using System.DirectoryServices;

      【讨论】:

      • 这个答案将始终返回 true(假设在返回之前有一个 members=true 行)
      【解决方案3】:

      解决方法 #1

      报告了此错误 here at Microsoft Connect 以及以下代码,可通过手动迭代 PrincipalSearchResult<Principal> 返回的对象、捕获此异常并继续处理来解决此问题:

      PrincipalSearchResult<Principal> groups = user.GetAuthorizationGroups();
      var iterGroup = groups.GetEnumerator();
      using (iterGroup)
      {
          while (iterGroup.MoveNext())
          {
              try
              {
                  Principal p = iterGroup.Current;
                  Console.WriteLine(p.Name);
              }
              catch (NoMatchingPrincipalException pex)
              {
                  continue;
              }
          }
      }
      

      解决方法 #2

      另一种解决方法found here 避免使用AccountManagement 类,而是使用System.DirectoryServices API:

      using System;  
      using System.Collections.Generic;  
      using System.Linq;  
      using System.Text;  
      using System.DirectoryServices;  
      
      namespace GetGroupsForADUser  
      {  
          class Program  
          {  
              static void Main(string[] args)  
              {  
                  String username = "Gabriel";  
      
                  List<string> userNestedMembership = new List<string>();  
      
                  DirectoryEntry domainConnection = new DirectoryEntry(); // Use this to query the default domain
                  //DirectoryEntry domainConnection = new DirectoryEntry("LDAP://example.com", "username", "password"); // Use this to query a remote domain
      
                  DirectorySearcher samSearcher = new DirectorySearcher();  
      
                  samSearcher.SearchRoot = domainConnection;  
                  samSearcher.Filter = "(samAccountName=" + username + ")";  
                  samSearcher.PropertiesToLoad.Add("displayName");  
      
                  SearchResult samResult = samSearcher.FindOne();  
      
                  if (samResult != null)  
                  {  
                      DirectoryEntry theUser = samResult.GetDirectoryEntry();  
                      theUser.RefreshCache(new string[] { "tokenGroups" });  
      
                      foreach (byte[] resultBytes in theUser.Properties["tokenGroups"])  
                      {  
                          System.Security.Principal.SecurityIdentifier mySID = new System.Security.Principal.SecurityIdentifier(resultBytes, 0);  
      
                          DirectorySearcher sidSearcher = new DirectorySearcher();  
      
                          sidSearcher.SearchRoot = domainConnection;  
                          sidSearcher.Filter = "(objectSid=" + mySID.Value + ")";  
                          sidSearcher.PropertiesToLoad.Add("distinguishedName");  
      
                          SearchResult sidResult = sidSearcher.FindOne();  
      
                          if (sidResult != null)  
                          {  
                              userNestedMembership.Add((string)sidResult.Properties["distinguishedName"][0]);  
                          }  
                      }  
      
                      foreach (string myEntry in userNestedMembership)  
                      {  
                          Console.WriteLine(myEntry);  
                      }  
      
                  }  
                  else 
                  {  
                      Console.WriteLine("The user doesn't exist");  
                  }  
      
                  Console.ReadKey();  
      
              }  
          }  
      }  
      

      【讨论】:

      • 我选择实施解决方法 #2。带有解决方法 #1 的 YMMV。
      • 好帖子。解决方法 #1 在 iterGroup.MoveNext() 上失败,并出现相同的错误“服务器上没有此类对象”。
      • 只有当程序从登录到域的计算机上运行时,解决方法 2 才有效。如果您从不同的域查询 ldap,它将无法工作。
      • @Kiquenet & @Ronen 看起来您可以为DirectoryEntry domainConnection 使用另一个构造函数来传递在不同域上有效的用户名和密码。见:stackoverflow.com/questions/9362724/…
      • 我编辑了答案以包含一行可用于连接到远程域的代码:DirectoryEntry domainConnection = new DirectoryEntry("LDAP://example.com", "username", "password"); // Use this to query a remote domain
      【解决方案4】:

      改用UserPrincipal.GetAuthorizationGroups() - 来自它的MSDN docs

      此方法搜索所有组 递归并返回组 用户是其成员。这 返回的集合还可能包括 该系统将附加的组 将用户视为 for 的成员 授权目的。

      由此返回的组 方法可能包括来自一个组 不同的范围和存储比 主要的。例如,如果 principal 是一个 AD DS 对象,它具有 一个 DN “CN=SpecialGroups,DC=Fabrikam,DC=com, 返回的集合可以包含组 属于 "CN=NormalGroups,DC=Fabrikam,DC=com.

      【讨论】:

      • 谢谢,但不幸的是,这会引发 PrincipalOperationException 异常,并显示消息“服务器上没有此类对象”。 UserPrincipal 肯定存在,因为我上面的方法确实返回了顶级组的正确授权。
      • UserPrincipal.GetAuthorizationGroups() 如果有用户仍然是其成员的已删除组,则会被阻塞
      • 在这种情况下检查 group.Name 是否不为空 @jproch
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-02
      相关资源
      最近更新 更多