Active Directory:获取所有组成员

时间:2018-03-06 17:48:29

标签: c# sql-server active-directory ldap ldap-query

问题:如何以一致的方式检索所有组成员?

上下文:我正在检索所有对象,组,联系人或计算机的对象:

Group Name      Retrieval Method    Recursive Search    Count Members   Comment
Group1          AccountManagement   TRUE                505 
Group1          AccountManagement   FALSE               505 
Group1          DirectoryServices   N/A                 101 
Group2          AccountManagement   TRUE                440             Contains group name 'Group3'
Group2          AccountManagement   FALSE               440             Contains group name 'Group3'
Group2          DirectoryServices   N/A                 100             Contains group name 'Group3'
Group3          AccountManagement   TRUE                101             All Group3
Group3          AccountManagement   FALSE               2               All Group3
Group3          DirectoryServices   N/A                 2               1 user 1 group (Group2)

我现在需要检索所有组成员。我已经开发了三种方法来做到这一点;然而,他们正在为同一组返回不同的结果,我不知道为什么。我怀疑它可能是由嵌套组(即组内的组)引起的。调试它是一个挑战,因为有些组包含这么多成员,调试器超时并且没有显示结果。

方法1和2很慢。方法3很快。所以,我更喜欢使用方法3.

S.DS.AM

方法1和2 :使用private static List<Guid> GetGroupMemberList(string strPropertyValue, string strDomainController, bool bolRecursive) { List<Guid> listGroupMemberGuid = null; GroupPrincipal groupPrincipal = null; PrincipalSearchResult<Principal> listPrincipalSearchResult = null; List<Principal> listPrincipalNoNull = null; PrincipalContext principalContext = null; ContextType contextType; IdentityType identityType; try { listGroupMemberGuid = new List<Guid>(); contextType = ContextType.Domain; principalContext = new PrincipalContext(contextType, strDomainController); identityType = IdentityType.Guid; groupPrincipal = GroupPrincipal.FindByIdentity(principalContext, identityType, strPropertyValue); if (groupPrincipal != null) { listPrincipalSearchResult = groupPrincipal.GetMembers(bolRecursive); listPrincipalNoNull = listPrincipalSearchResult.Where(item => item.Name != null).ToList(); foreach (Principal principal in listPrincipalNoNull) { listGroupMemberGuid.Add((Guid)principal.Guid); } } return listGroupMemberGuid; } catch (MultipleMatchesException) { throw new MultipleMatchesException(strPropertyValue); } catch (Exception ex) { throw ex; } finally { listGroupMemberGuid = null; listPrincipalSearchResult.Dispose(); principalContext.Dispose(); groupPrincipal.Dispose(); } } 获取小组成员,其中GetMembers()分别设置为true或false:https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement(v=vs.110).aspx

S.DS.AD

方法3 :使用private static List<string> GetGroupMemberList(string strPropertyValue, string strActiveDirectoryHost, int intActiveDirectoryPageSize) { List<string> listGroupMemberDn = new List<string>(); string strPath = strActiveDirectoryHost + "/<GUID=" + strPropertyValue + ">"; DirectoryEntry directoryEntryGroup; DirectoryEntry directoryEntryGroupMembers; DirectorySearcher directorySearcher; SearchResultCollection searchResultCollection; DataTypeConverter objConverter = null; objConverter = new DataTypeConverter(); try { directoryEntryGroup = new DirectoryEntry(strPath, null, null, AuthenticationTypes.Secure); directoryEntryGroup.RefreshCache(); } catch (Exception ex) { throw ex; } try { directorySearcher = new DirectorySearcher(directoryEntryGroup) { //Filter = "(objectCategory=group)", // Group SearchScope = SearchScope.Subtree, PageSize = intActiveDirectoryPageSize, }; directorySearcher.PropertiesToLoad.Add("objectGUID"); searchResultCollection = directorySearcher.FindAll(); } catch (Exception ex) { throw ex; } try { foreach (SearchResult searchResult in searchResultCollection) { directoryEntryGroupMembers = searchResult.GetDirectoryEntry(); foreach (object objGroupMember in directoryEntryGroupMembers.Properties["member"]) { listGroupMemberDn.Add((string)objGroupMember); } } return listGroupMemberDn; } catch (Exception ex) { throw ex; } finally { listGroupMemberDn = null; strPath = null; directoryEntryGroup.Dispose(); directoryEntryGroupMembers = null; directorySearcher.Dispose(); searchResultCollection.Dispose(); objConverter = null; } } 获取小组成员:https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectory(v=vs.110).aspx

 private static List<string> GetGroupMemberList(string strPropertyValue, string strActiveDirectoryHost, int intActiveDirectoryPageSize)
    {
        // Variable declaration(s).
        List<string> listGroupMemberDn = new List<string>();
        string strPath = strActiveDirectoryHost + "/<GUID=" + strPropertyValue + ">";
        string strMemberPropertyRange = null;
        DirectoryEntry directoryEntryGroup = null;
        DirectorySearcher directorySearcher = null;
        SearchResultCollection searchResultCollection = null;
        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms676302(v=vs.85).aspx
        const int intIncrement = 1500;

        // Load the DirectoryEntry.
        try
        {
            // Setup a secure connection with Active Directory (AD) using Kerberos by setting the directoryEntry with AuthenticationTypes.Secure.
            directoryEntryGroup = new DirectoryEntry(strPath, null, null, AuthenticationTypes.Secure);

            // Load the property values for this DirectoryEntry object into the property cache.
            directoryEntryGroup.RefreshCache();
        }
        catch (Exception)
        { }

        #region Method1
        // Enumerate group members.
        try
        {
            // Check to see if the group has any members.
            if (directoryEntryGroup.Properties["member"].Count > 0)
            {
                int intStart = 0;
                while (true)
                {
                    // End of the range.
                    int intEnd = intStart + intIncrement - 1;

                    strMemberPropertyRange = string.Format("member;range={0}-{1}", intStart, intEnd);

                    directorySearcher = new DirectorySearcher(directoryEntryGroup)
                    {
                        // Set the Filter criteria that is used to constrain the search within AD.
                        Filter = "(|(objectCategory=person)(objectCategory=computer)(objectCategory=group))", // User, Contact, Group, Computer objects

                        // Set the SearchScope for how the AD tree is searched (Default = Subtree).
                        SearchScope = SearchScope.Base,

                        // The PageSize value should be set equal to the PageSize that is set by the AD administrator (Default = 0).
                        PageSize = intActiveDirectoryPageSize,

                        PropertiesToLoad = { strMemberPropertyRange }
                    };

                    try
                    {
                        // Populate the searchResultCollection with all records within AD that match the Filter criteria.
                        searchResultCollection = directorySearcher.FindAll();

                        foreach (SearchResult searchResult in searchResultCollection)
                        {
                            var membersProperties = searchResult.Properties;

                            var membersPropertyNames = membersProperties.PropertyNames.OfType<string>().Where(n => n.StartsWith("member;"));

                            foreach (var propertyName in membersPropertyNames)
                            {
                                // Get all members from the ranged result.
                                var members = membersProperties[propertyName];

                                foreach (string memberDn in members)
                                {
                                    // Add the member's "distinguishedName" attribute value to the list.
                                    listGroupMemberDn.Add(memberDn);
                                }
                            }
                        }
                    }
                    catch (DirectoryServicesCOMException)
                    {
                        // When the start of the range exceeds the number of available results, an exception is thrown and we exit the loop.
                        break;
                    }

                    // Increment for the next range.
                    intStart += intIncrement;
                }
            }

            // return the listGroupMemberDn;
            return listGroupMemberDn;
        }
        #endregion

        finally
        {
            // Cleanup objects.
            listGroupMemberDn = null;
            strPath = null;
            strMemberPropertyRange = null;
            directoryEntryGroup.Dispose();
            directorySearcher.Dispose();
            searchResultCollection.Dispose();
        }
    }

方法4 :(使用GetNextChunk()方法实现循环)

LoadControlsCallback

1 个答案:

答案 0 :(得分:1)

System.DirectoryServices.AccountManagement可以更方便,因为它隐藏了AD的大部分复杂性,但这也是它变慢的原因。你无法控制正在发生的事情。

DirectoryEntry为您提供更多控制权,但您必须处理一些复杂性。

这样可以解释时差。

但是使用DirectoryEntry的方法看起来仍然过于复杂。为什么要使用DirectorySearcher?它似乎没有添加任何东西。设置directoryEntryGroup时,您已拥有该组。之后,您可以访问成员:

foreach (var member in directoryEntryGroup.Properties["member"]) {
    //member is a string of the distinguishedName
}

对于非常大的群体,请注意默认情况下,AD将其返回的记录限制为1500.因此,一旦您点击该数字,您将不得不要求更多。你这样做:

directoryEntryGroup.RefreshCache("member;range=1500-*")

然后以同样的方式再次循环它们。如果你得到另外1500,然后要求更多(用3000替换1500)等等,直到你拥有它们。

这正是System.DirectoryServices.AccountManagement的.NET核心实现所做的(我认为.NET 4.x也是如此 - 我只是看不到代码)。您可以在此处看到.NET Core代码用于执行此操作的特殊类(请参阅GetNextChunk方法):https://github.com/dotnet/corefx/blob/0eb5e7451028e9374b8bb03972aa945c128193e1/src/System.DirectoryServices.AccountManagement/src/System/DirectoryServices/AccountManagement/AD/RangeRetriever.cs

作为旁注:

catch (Exception ex)
{
    // Something went wrong. Throw an error.
    throw ex;
}

如果您要在不做任何其他事情的情况下重新抛出异常,请不要抓住它。重新抛出具有隐藏异常实际发生位置的效果,因为您的堆栈跟踪现在会说明异常发生在throw ex;,而不是告诉您发生异常的实际行。

即使是最后一个阻止版,也可以使用try / finally而不使用catch