在我的程序中,我需要获取用户的所有AD组。 我程序的当前版本使用System.DirectoryServices.AccountManagement.UserPrincipal.GetAuthorizationGroups。
这很好,直到我们有了一个新客户,其AD更大。那里真的很慢。 (最长60秒)
现在我一直在四处看看,看到了AccountManagement易于使用但速度很慢的帖子。
我还发现LDAP_MATCHING_RULE_IN_CHAIN还应该获取用户所属的所有嵌套组。而且性能更高。
通过this链接。
但是我对AD中存在的默认组有疑问。
例如,该函数未返回“域用户”组。 他们还具有一个组“ BDOC”,该组作为成员具有“域用户”。该组也不会返回。
通过GetAuthorizationGroups可以正确返回。
我正在使用以下代码按用户获取组。
VB.NET:
Dim strFilter As String = String.Format("(member:1.2.840.113556.1.4.1941:={0})", oUserPrincipal.DistinguishedName)
Dim objSearcher As New DirectoryServices.DirectorySearcher("LDAP://" & oLDAPAuthenticationDetail.Domain & If(Not String.IsNullOrWhiteSpace(oLDAPAuthenticationDetail.Container), oLDAPAuthenticationDetail.Container, String.Empty))
objSearcher.PageSize = 1000
objSearcher.Filter = strFilter
objSearcher.SearchScope = DirectoryServices.SearchScope.Subtree
objSearcher.PropertiesToLoad.Add(sPropGuid)
objSearcher.PropertiesToLoad.Add(sPropDisplayName)
Dim colResults As DirectoryServices.SearchResultCollection = objSearcher.FindAll()
此后,我通过链接中的脚本进行了测试,是否有可能通过将过滤器中的“成员”更改为“成员”来从“域用户”组中获取所有用户。 当我将Domain Admins组放入过滤器时,它显示管理员正确。 当我将“域用户”组放入过滤器时,它什么也不返回。
Powershell:
$userdn = 'CN=Domain Users,CN=Users,DC=acbenelux,DC=local'
$strFilter = "(memberOf:1.2.840.113556.1.4.1941:=$userdn)"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry("LDAP://rootDSE")
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = "LDAP://$($objDomain.rootDomainNamingContext)"
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = "Base"
$colProplist = "name"
foreach ($i in $colPropList)
{
$objSearcher.PropertiesToLoad.Add($i) > $nul
}
$colResults = $objSearcher.FindAll()
foreach ($objResult in $colResults)
{
$objItem = $objResult.Properties
$objItem.name
}
我不知道我在做什么错。还是可能无法使用该过滤器获取“默认组”? 那有什么好的选择呢?
答案 0 :(得分:1)
默认组为奇数。它没有存储在memberOf
中,甚至没有存储在组的member
属性中。这就是为什么您的搜索找不到它的原因。默认组由用户的primaryGroupId
确定。该属性存储组的RID(SID的最后一部分)。我知道这有点愚蠢:)
我实际上写了一篇关于某人可以成为组成员的3种方式(是3种)的文章:What makes a member a member?
我还写了一篇文章,介绍如何获取单个用户所属的所有组,以及如何说明所有3种方式:Finding all of a user’s groups
例如,这是我在该文章中输入的C#代码,该代码有关如何查找用户的主要组的名称(给定DirectoryEntry
)。将其转换为VB.NET并不难:
private static string GetUserPrimaryGroup(DirectoryEntry de) {
de.RefreshCache(new[] {"primaryGroupID", "objectSid"});
//Get the user's SID as a string
var sid = new SecurityIdentifier((byte[])de.Properties["objectSid"].Value, 0).ToString();
//Replace the RID portion of the user's SID with the primaryGroupId
//so we're left with the group's SID
sid = sid.Remove(sid.LastIndexOf("-", StringComparison.Ordinal) + 1);
sid = sid + de.Properties["primaryGroupId"].Value;
//Find the group by its SID
var group = new DirectoryEntry($"LDAP://<SID={sid}>");
group.RefreshCache(new [] {"cn"});
return group.Properties["cn"].Value as string;
}
您很正确,AccountManagement
名称空间使事情变得容易,但有时确实确实具有糟糕的性能。我不再使用它了。我发现DirectoryEntry
/ DirectorySearcher
使您可以更好地控制代码向AD发出呼叫的频率。
我一直想写一篇有关用DirectoryEntry
编写高性能代码的文章,但是我还没有解决。
更新:因此,如果您需要用户的嵌套组(包括通过主要组的成员资格),则可以先找到主要组,然后执行LDAP_MATCHING_RULE_IN_CHAIN搜索具有两个用户和主要组作为成员:
(|(member:1.2.840.113556.1.4.1941:={userDN})(member:1.2.840.113556.1.4.1941:={primaryGroupDN}))
更新::如果要在搜索中包括Authenticated Users
(编辑DC
的{{1}}部分):
distinguishedName
请注意,您还可以使用用户的 (|(member:1.2.840.113556.1.4.1941:=CN=S-1-5-11,CN=ForeignSecurityPrincipals,DC=domain,DC=com)(member:1.2.840.113556.1.4.1941:={userDN})(member:1.2.840.113556.1.4.1941:={primaryGroupDN})
属性来查找用户的所有身份验证组。 tokenGroups
属性是一个构造属性,因此只有在您明确要求时才使用它(使用DirectoryEntry.RefreshCache()
或在搜索中将其添加到DirectorySearcher.PropertiesToLoad
。
需要注意的是,tokenGroups
是SID的列表,而不是tokenGroups
,但是您可以使用distinguishedName
语法的SID直接绑定到对象。