gpt4 book ai didi

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

转载 作者:太空狗 更新时间:2023-10-29 21:14:29 24 4
gpt4 key购买 nike

在网络应用程序中,我们希望为属于特定组的用户显示 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 命名空间(GroupPrincipalUserPrincipal 等) .它使编码更容易,但它是 slloooowwww。

仅使用 DirectorySearcherDirectoryEntry。 (无论如何,AccountManagement 命名空间只是一个包装器)

不久前我和其他人讨论过这个问题。你可以阅读the full chat here , 但在一个组有 4873 名成员的情况下,AccountManagementGetMember() 方法耗时 200 秒,而使用 DirectoryEntry 仅耗时 16秒。

但是,有一些注意事项:

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

AccountManagement 命名空间的 GetMember() 方法会处理所有这些事情,只是没有尽可能高效。

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

更新:

(我已经在我的网站上记录了所有这些:Find all the members of a group)

您提到最耗时的部分是遍历成员。那是因为你绑定(bind)到每个成员,这是可以理解的。您可以通过在 DirectoryEntry 对象上调用 .RefreshCache() 以仅加载您需要的属性来减少这种情况。否则,当您第一次使用 Properties 时,它会获取每个具有值的属性,这无缘无故地增加了时间。

下面是我使用的一个例子。我测试了一个有 803 名成员(在嵌套组中)的组,发现 .RefreshCache() 行持续减少大约 10 秒,如果不是更多的话(~60s 没有,~45-50s与)。

此方法不会考虑我上面提到的第 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;
}

关于c# - 使用 C# 快速获取 Active Directory 中组成员列表的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6474276/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com