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

如何解决使用C#在Active Directory中获取组成员列表的快速方法

|| 在网络应用中,我们希望为属于特定组的用户显示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());
}
    

解决方法

        我的一个同事在使用各种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,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();
} 对于@Gabriel Luci评论:Microsoft文档 成员   memberOf属性是一个多值属性,包含   用户是直接成员的组,主要用户除外   组,由primaryGroupId表示。群组成员资格为   取决于此属性来自的域控制器(DC)   检索到:         在包含用户的域的DC上,该用户的memberOf   该域中组的成员资格已完成;   但是,memberOf不包含域中用户的成员身份   其他域中的本地和全球组。   在GC服务器上,memberOf用于   用户对所有通用组成员身份都是完整的。   如果DC的两个条件均成立,则两组数据均为   包含在memberOf中。         请注意,此属性列出了包含用户的组   其成员属性-它不包含的递归列表   嵌套的前辈。例如,如果用户O是C组的成员,而   B组和B组嵌套在A组中,其中的memberOf属性   用户O将列出组C和组B,但不列出组A。      此属性未存储-它是计算的反向链接属性。     ,        尝试不确定是否会更快,但是...
        PrincipalContext pcRoot = new PrincipalContext(ContextType.Domain)

        GroupPrincipal mygroup = new GroupPrincipal(pcRoot);

        // define the principal searcher,based on that example principal

        PrincipalSearcher ps = new PrincipalSearcher(mygroup);

        ps.QueryFilter = new GroupPrincipal(pcRoot) { SamAccountName = \"Name of your group Case Sensitive\" };

        List<UserPrincipal> users = new List<UserPrincipal>();
        // loop over all principals found by the searcher

        GroupPrincipal foundGroup = (GroupPrincipal)ps.FindOne();

foreach (UserPrincipal u in foundGroup.Members) 
                    {
                        users.Add(u);

                    }
//OR
List<string> lst = foundGroup.Members.Select(g => g.SamAccountName).ToList();//this will only get the usernames not the user object or UserPrincipal
    ,如果要提高速度,请不要使用
System.DirectoryServices.AccountManagement
命名空间(
GroupPrincipal
UserPrincipal
等)。它使编码更容易,但是它很简单。 仅使用
DirectorySearcher
DirectoryEntry
。 (无论如何,“ 10”命名空间只是一个包装) 我不久前与其他人进行了讨论。您可以在此处阅读完整的聊天记录,但在一个小组有4873名成员的情况下,
AccountManagement
GetMember()
方法花费了200秒,而using9ѭ的花费仅16秒。 但是,有一些警告: 不要看“ 14”属性(正如JPBlanc的答案所建议的)。它不会找到“本地域”组的成员。
memberOf
属性仅在同一域上显示通用组和全局组。域本地组未显示在此处。 查看组的“ 16”属性只会一次为您提供1500个成员。您必须以1500为批次检索成员。 一个帐户可能将ѭ17设置为任何组,并被视为该组的一部分(但不会显示在该组的
member
属性中)。通常只有
Domain Users
组的情况如此。 如果“本地域”组中有来自外部域的用户,则这些用户将显示为“外部安全主体”对象,这些对象保存外部域上实际帐户的SID。需要做一些额外的工作才能在外部域上找到该帐户。
AccountManagement
名称空间的
GetMember()
方法会处理所有这些事情,只是效率不高。 在帮助其他用户时,我确实提出了一种方法,该方法将涵盖上述前三个问题,但不适用于#4。这是此答案中的最后一个代码块:https://stackoverflow.com/a/49241443/1202807 更新: (我已经在我的网站上记录了所有这些信息:查找组中的所有成员) 您提到了最耗时的部分是遍历成员。那是因为您绑定到每个成员,这是可以理解的。您可以通过在
DirectoryEntry
对象上调用
.RefreshCache()
以仅加载所需的属性来减轻这种情况。否则,当您第一次使用
Properties
时,它将获得每个具有值的属性,这会无缘无故地增加时间。 以下是我使用的示例。我在一个有803个成员的组中进行了测试(嵌套组),发现found22ѭ线持续减少了大约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;
}
    ,        您是否尝试过LDAP查询?页面底部有一个C#示例,用于枚举组以获取成员。 MSDN BOL
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;
    }
}
}     ,        类似于您的第一个选项,我从列表中创建了一个哈希集。组越大,验证成员资格所需的时间越长。但是,它对于成功和不成功的成员资格查询都是一致的。如果该帐户不是会员,则要遍历一个大型组有时会花费3倍的时间,而此方法每次都相同。
using(PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
using(GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx,IdentityType.SamAccountName,\"groupName\"))
{
    List<string> members = group.GetMembers(true).Select(g => g.SamAccountName).ToList();
    HashSet<string> hashset = new HashSet<string>(members,StringComparer.OrdinalIgnoreCase);

    if(hashset.Contains(someUser)
        return true;
}
缓存组成员 Active Directory中的组成员身份不应经常更改。因此,请考虑缓存组成员以加快查找速度。然后每小时更新缓存的组成员身份,或者对您的环境最有意义的更新。这将大大提高性能并减少网络和域控制器上的拥塞。 一个警告是,重要/受限制的信息是否受到保护,并且需要更强大的安全控制。然后直接查询Active Directory是可行的方法,因为它可以确保您拥有最新的成员资格信息。     

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-