使用 C# 快速获取 Active Directory 中的组成员列表

2023-12-24

在网络应用程序中,我们希望显示属于特定组的用户的 sam 帐户列表。在许多情况下,群组可能有 500 名或更多成员,我们需要页面具有响应能力。

对于大约有 500 名成员的群组,需要 7-8 秒才能获取该群组所有成员的 sam 帐户列表。有更快的方法吗?我知道 Active Directory 管理控制台在一秒钟之内就能完成。

我尝试了几种方法:

1)

PrincipalContext pcRoot = new PrincipalContext(ContextType.Domain)
GroupPrincipal grp = GroupPrincipal.FindByIdentity(pcRoot, "MyGroup");
List<string> lst = grp.Members.Select(g => g.SamAccountName).ToList();

2)

PrincipalContext pcRoot = new PrincipalContext(ContextType.Domain)
GroupPrincipal grp = GroupPrincipal.FindByIdentity(pcRoot, "MyGroup");
PrincipalSearchResult<Principal> lstMembers = grp.GetMembers(true);
List<string> lst = new List<string>();
foreach (Principal member in lstMembers )
{
    if (member.StructuralObjectClass.Equals("user"))
    {
        lst.Add(member .SamAccountName);
    }
}

3)

PrincipalContext pcRoot = new PrincipalContext(ContextType.Domain)
GroupPrincipal grp = GroupPrincipal.FindByIdentity(pcRoot, "MyGroup");
System.DirectoryServices.DirectoryEntry de = (System.DirectoryServices.DirectoryEntry)grp.GetUnderlyingObject();
List<string> lst = new List<string>();
foreach (string sDN in de.Properties["member"])
{
    System.DirectoryServices.DirectoryEntry deMember = new System.DirectoryServices.DirectoryEntry("LDAP://" + sDN);
    lst.Add(deMember.Properties["samAccountName"].Value.ToString());
}

如果你想要速度,不要使用System.DirectoryServices.AccountManagement完全命名空间(GroupPrincipal, UserPrincipal, ETC。)。它使编码变得更容易,但它太慢了。

仅供使用DirectorySearcher and DirectoryEntry. (The AccountManagement无论如何,命名空间只是一个包装器)

不久前我和别人讨论过这个问题。你可以阅读完整的聊天内容在这里 https://chat.stackoverflow.com/rooms/166716/discussion-between-gabriel-luci-and-j-weezy,但在一个群组有 4873 名成员的情况下,AccountManagement's GetMember()方法花费了 200 秒,其中使用DirectoryEntry仅用了16秒。

但是,有一些注意事项:

  1. 不要看memberOf属性(正如 JPBlanc 的回答所示)。它不会找到域本地组的成员。这memberOf属性仅显示通用组,全局组仅显示在同一域中。域本地组不会显示在那里。
  2. 看着member一个群组的属性一次只能为您提供 1500 名成员。您必须以 1500 个为批次检索成员。
  3. 一个帐户可能有primaryGroupId设置为任何组,并被视为该组的一部分(但不会出现在member该组的属性)。通常只有以下情况才会出现这种情况Domain Users group.
  4. 如果域本地组有来自外部域的用户,它们将显示为外部安全主体对象,该对象保存外部域上实际帐户的 SID。需要完成一些额外的工作才能在外部域上查找帐户。

The AccountManagement命名空间的GetMember()方法可以处理所有这些事情,只是效率不高。

在帮助其他用户时,我确实整理了一个方法来解决上面的前三个问题,但不包括#4。这是这个答案中的最后一个代码块:https://stackoverflow.com/a/49241443/1202807 https://stackoverflow.com/a/49241443/1202807

Update:

(我已在我的网站上记录了所有这些:查找群组的所有成员 https://www.gabescode.com/active-directory/2018/11/30/find-all-members-of-group.html)

您提到最耗时的部分是循环访问成员。那是因为你对每个成员都有约束力,这是可以理解的。您可以通过致电来减少这种情况.RefreshCache() on the DirectoryEntry对象仅加载您需要的属性。否则,当你第一次使用时Properties,它将获取每个具有值的属性,这会无缘无故地增加时间。

下面是我使用的一个例子。我对一个有 803 名成员的组(在嵌套组中)进行了测试,发现具有.RefreshCache()线路持续缩短约 10 秒,甚至更多(不使用约 60 秒,使用时约 45-50 秒)。

此方法不会考虑我上面提到的第 3 点和第 4 点。例如,它会默默地忽略外国安全主体。但如果您只有一个没有信任的域,则无需关心。

private static List<string> GetGroupMemberList(DirectoryEntry group, bool recurse = false) {
    var members = new List<string>();

    group.RefreshCache(new[] { "member" });

    while (true) {
        var memberDns = group.Properties["member"];
        foreach (var member in memberDns) {
            var memberDe = new DirectoryEntry($"LDAP://{member}");
            memberDe.RefreshCache(new[] { "objectClass", "sAMAccountName" });
            if (recurse && memberDe.Properties["objectClass"].Contains("group")) {
                members.AddRange(GetGroupMemberList(memberDe, true));
            } else {
                var username = memberDe.Properties["sAMAccountName"]?.Value?.ToString();
                if (!string.IsNullOrEmpty(username)) { //It will be null if this is a Foreign Security Principal
                    members.Add(username);
                }
            }
        }

        if (memberDns.Count == 0) break;

        try {
            group.RefreshCache(new[] {$"member;range={members.Count}-*"});
        } catch (COMException e) {
            if (e.ErrorCode == unchecked((int) 0x80072020)) { //no more results
                break;
            }
            throw;
        }
    }

    return members;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 C# 快速获取 Active Directory 中的组成员列表 的相关文章

随机推荐