Jer*_*emy 6 .net c# active-directory
在Web应用程序中,我们希望显示属于特定组成员的用户的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();
Run Code Online (Sandbox Code Playgroud)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
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());
}
Run Code Online (Sandbox Code Playgroud)
小智 5
使用各种Active Directory检索方法时,我的同事在查询时间方面遇到类似问题.他最终将信息缓存在数据库中并每晚刷新,然后只是访问数据库.
考虑到用户帐户不会经常改变,这对他来说是一个可接受的妥协.根据您的使用情况,这可能是也可能是不可接受的.
这是使用ADSI的递归搜索(嵌套组中的搜索用户)。
static void Main(string[] args)
{
/* Connection to Active Directory
*/
string sFromWhere = "LDAP://SRVENTR2:389/dc=societe,dc=fr";
DirectoryEntry deBase = new DirectoryEntry(sFromWhere, "societe\\administrateur", "test.2011");
/* To find all the users member of groups "Grp1" :
* Set the base to the groups container DN; for example root DN (dc=societe,dc=fr)
* Set the scope to subtree
* Use the following filter :
* (member:1.2.840.113556.1.4.1941:=CN=Grp1,OU=MonOu,DC=X)
*/
DirectorySearcher dsLookFor = new DirectorySearcher(deBase);
dsLookFor.Filter = "(&(memberof:1.2.840.113556.1.4.1941:=CN=Grp1,OU=MonOu,DC=societe,DC=fr)(objectCategory=user))";
dsLookFor.SearchScope = SearchScope.Subtree;
dsLookFor.PropertiesToLoad.Add("cn");
dsLookFor.PropertiesToLoad.Add("samAccountName");
SearchResultCollection srcUsers = dsLookFor.FindAll();
/* Just show each user
*/
foreach (SearchResult srcUser in srcUsers)
{
Console.WriteLine("{0}", srcUser.Path);
Console.WriteLine("{0}", srcUser.Properties["samAccountName"][0]);
}
Console.ReadLine();
Run Code Online (Sandbox Code Playgroud)
}
对于@Gabriel Luci评论:Microsoft文档
成员
memberOf属性是一个多值属性,其中包含用户为直接成员的组,但由primaryGroupId表示的主要组除外。组成员身份取决于从中获取此属性的域控制器(DC):
在包含用户的域的DC上,对于该域中组的成员身份,该用户的memberOf是完整的。但是,memberOf不包含用户在其他域中的本地和全局组中的成员身份。
在GC服务器上,对于用户的memberOf对于所有通用组成员身份都是完整的。如果DC的两个条件都成立,则这两个数据集都包含在memberOf中。
请注意,此属性在其成员属性中列出了包含用户的组-它不包含嵌套前辈的递归列表。例如,如果用户O是组C的成员,并且组B和组B嵌套在组A中,则用户O的memberOf属性将列出组C和组B,而不列出组A。
此属性未存储-它是计算的反向链接属性。
如果你想要的速度,不要使用System.DirectoryServices.AccountManagement在所有(命名空间GroupPrincipal,UserPrincipal等等)。它使编码更容易,但它是slloooowwww。
仅使用DirectorySearcher和DirectoryEntry。(AccountManagement无论如何,命名空间只是一个包装器)
不久前我和别人讨论过这个问题。你可以在这里阅读完整的聊天记录,但在一个群组有 4873 名成员的情况下,AccountManagement的GetMember()方法需要 200 秒,而使用DirectoryEntry只需要 16 秒。
但是,有一些注意事项:
memberOf属性(正如 JPBlanc 的回答所暗示的那样)。它不会找到域本地组的成员。该memberOf属性仅显示通用组,而全局组仅显示在同一域中。域本地组不会出现在那里。member一个组的属性一次只能为您提供1500个成员。您必须分批检索 1500 个成员。primaryGroupId设置设置为任何组,并被视为该组的一部分(但不会显示在该member组的属性中)。这通常是Domain Users组的情况。该AccountManagement命名空间的GetMember()方法照顾到所有的这些事情,只是没有有效的,因为它可以。
在帮助其他用户时,我确实组合了一个方法来涵盖上述前三个问题,但不包括 #4。这是此答案中的最后一个代码块:https : //stackoverflow.com/a/49241443/1202807
更新:
(我已经在我的网站上记录了所有这些:查找组的所有成员)
您提到最耗时的部分是遍历成员。那是因为您绑定到每个成员,这是可以理解的。你可以通过调用减轻这种.RefreshCache()在上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;
}
Run Code Online (Sandbox Code Playgroud)
小智 0
您尝试过 LDAP 查询吗?页面底部有一个 C# 示例,用于枚举组以获取成员。 MSDN博尔
using System;
using System.DirectoryServices;
namespace ADAM_Examples
{
class EnumMembers
{
/// <summary>
/// Enumerate AD LDS groups and group members.
/// </summary>
[STAThread]
static void Main()
{
DirectoryEntry objADAM; // Binding object.
DirectoryEntry objGroupEntry; // Group Results.
DirectorySearcher objSearchADAM; // Search object.
SearchResultCollection objSearchResults; // Results collection.
string strPath; // Binding path.
// Construct the binding string.
strPath = "LDAP://localhost:389/OU=TestOU,O=Fabrikam,C=US";
Console.WriteLine("Bind to: {0}", strPath);
Console.WriteLine("Enum: Groups and members.");
// Get the AD LDS object.
try
{
objADAM = new DirectoryEntry(strPath);
objADAM.RefreshCache();
}
catch (Exception e)
{
Console.WriteLine("Error: Bind failed.");
Console.WriteLine(" {0}", e.Message);
return;
}
// Get search object, specify filter and scope,
// perform search.
try
{
objSearchADAM = new DirectorySearcher(objADAM);
objSearchADAM.Filter = "(&(objectClass=group))";
objSearchADAM.SearchScope = SearchScope.Subtree;
objSearchResults = objSearchADAM.FindAll();
}
catch (Exception e)
{
Console.WriteLine("Error: Search failed.");
Console.WriteLine(" {0}", e.Message);
return;
}
// Enumerate groups and members.
try
{
if (objSearchResults.Count != 0)
{
foreach(SearchResult objResult in objSearchResults)
{
objGroupEntry = objResult.GetDirectoryEntry();
Console.WriteLine("Group {0}",
objGroupEntry.Name);
foreach(object objMember
in objGroupEntry.Properties["member"])
{
Console.WriteLine(" Member: {0}",
objMember.ToString());
}
}
}
else
{
Console.WriteLine("Results: No groups found.");
}
}
catch (Exception e)
{
Console.WriteLine("Error: Enumerate failed.");
Console.WriteLine(" {0}", e.Message);
return;
}
Console.WriteLine("Success: Enumeration complete.");
return;
}
}
Run Code Online (Sandbox Code Playgroud)
}
| 归档时间: |
|
| 查看次数: |
23773 次 |
| 最近记录: |