繁体   English   中英

如何获取特定用户的所有 AD 组?

[英]How to get all the AD groups for a particular user?

我已经检查了这篇文章。 但它没有回答我的问题。 我想获取特定用户所属的所有活动目录组。

我写了下面的代码。 但我无法继续进行,因为我不知道如何提供过滤器以及如何访问属性。

class Program
{
    static void Main(string[] args)
    {
        DirectoryEntry de = new DirectoryEntry("LDAP://mydomain.com");
        DirectorySearcher searcher = new DirectorySearcher(de);
        searcher.Filter = "(&(ObjectClass=group))";
        searcher.PropertiesToLoad.Add("distinguishedName");
        searcher.PropertiesToLoad.Add("sAMAccountName");
        searcher.PropertiesToLoad.Add("name");
        searcher.PropertiesToLoad.Add("objectSid");
        SearchResultCollection results = searcher.FindAll();
        int i = 1;
        foreach (SearchResult res in results)
        {
            Console.WriteLine("Result" + Convert.ToString(i++));
            DisplayProperties("distinguishedName", res);
            DisplayProperties("sAMAccouontName", res);
            DisplayProperties("name", res);
            DisplayProperties("objectSid", res);
            Console.WriteLine();
        }

        Console.ReadKey();
    }

    private static void DisplayProperties(string property, SearchResult res)
    {
        Console.WriteLine("\t" + property);
        ResultPropertyValueCollection col = res.Properties[property];
        foreach (object o in col)
        {
            Console.WriteLine("\t\t" + o.ToString());
        }
    }
}

有任何想法吗?

您应该使用System.DirectoryServices.AccountManagement 这要容易得多。 这是一篇不错的代码项目文章,为您概述了此 DLL 中的所有类。

正如您所指出的,您目前的方法没有找出主要群体。 事实上,情况比你想象的要糟糕得多。 还有一些它不起作用的情况,例如来自另一个域的域本地组。 您可以 在此处查看详细信息。 如果您切换到使用 System.DirectoryServices.AccountManagement,代码如下所示。 以下代码可以找到此用户分配到的直接组,其中包括主要组。

UserPrincipal user = UserPrincipal.FindByIdentity(new PrincipalContext (ContextType.Domain, "mydomain.com"), IdentityType.SamAccountName, "username");
foreach (GroupPrincipal group in user.GetGroups())
{
    Console.Out.WriteLine(group);
}

使用 令牌组

DirectorySearcher ds = new DirectorySearcher();
ds.Filter = String.Format("(&(objectClass=user)(sAMAccountName={0}))", username);
SearchResult sr = ds.FindOne();

DirectoryEntry user = sr.GetDirectoryEntry();
user.RefreshCache(new string[] { "tokenGroups" });

for (int i = 0; i < user.Properties["tokenGroups"].Count; i++) {
    SecurityIdentifier sid = new SecurityIdentifier((byte[]) user.Properties["tokenGroups"][i], 0);
    NTAccount nt = (NTAccount)sid.Translate(typeof(NTAccount));
    //do something with the SID or name (nt.Value)
}

注意:这只会获取安全组

只需查询“memberOf”属性并遍历返回值,例如:

            search.PropertiesToLoad.Add("memberOf");
            StringBuilder groupNames = new StringBuilder(); //stuff them in | delimited

                SearchResult result = search.FindOne();
                int propertyCount = result.Properties["memberOf"].Count;
                String dn;
                int equalsIndex, commaIndex;

                for (int propertyCounter = 0; propertyCounter < propertyCount;
                    propertyCounter++)
                {
                    dn = (String)result.Properties["memberOf"][propertyCounter];

                    equalsIndex = dn.IndexOf("=", 1);
                    commaIndex = dn.IndexOf(",", 1);
                    if (-1 == equalsIndex)
                    {
                        return null;
                    }
                    groupNames.Append(dn.Substring((equalsIndex + 1),
                                (commaIndex - equalsIndex) - 1));
                    groupNames.Append("|");
                }

            return groupNames.ToString();

这只是将组名填充到 groupNames 字符串中,以管道分隔,但是当你旋转时,你可以对它们做任何你想做的事情

这段代码运行得更快(两个 1.5 比我以前的版本快):

    public List<String> GetUserGroups(WindowsIdentity identity)
    {
        List<String> groups = new List<String>();

        String userName = identity.Name;
        int pos = userName.IndexOf(@"\");
        if (pos > 0) userName = userName.Substring(pos + 1);

        PrincipalContext domain = new PrincipalContext(ContextType.Domain, "riomc.com");
        UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, userName); // NGeodakov

        DirectoryEntry de = new DirectoryEntry("LDAP://RIOMC.com");
        DirectorySearcher search = new DirectorySearcher(de);
        search.Filter = "(&(objectClass=group)(member=" + user.DistinguishedName + "))";
        search.PropertiesToLoad.Add("cn");
        search.PropertiesToLoad.Add("samaccountname");
        search.PropertiesToLoad.Add("memberOf");

        SearchResultCollection results = search.FindAll();
        foreach (SearchResult sr in results)
        {
            GetUserGroupsRecursive(groups, sr, de);
        }

        return groups;
    }

    public void GetUserGroupsRecursive(List<String> groups, SearchResult sr, DirectoryEntry de)
    {
        if (sr == null) return;

        String group = (String)sr.Properties["cn"][0];
        if (String.IsNullOrEmpty(group))
        {
            group = (String)sr.Properties["samaccountname"][0];
        }
        if (!groups.Contains(group))
        {
            groups.Add(group);
        }

        DirectorySearcher search;
        SearchResult sr1;
        String name;
        int equalsIndex, commaIndex;
        foreach (String dn in sr.Properties["memberof"])
        {
            equalsIndex = dn.IndexOf("=", 1);
            if (equalsIndex > 0)
            {
                commaIndex = dn.IndexOf(",", equalsIndex + 1);
                name = dn.Substring(equalsIndex + 1, commaIndex - equalsIndex - 1);

                search = new DirectorySearcher(de);
                search.Filter = "(&(objectClass=group)(|(cn=" + name + ")(samaccountname=" + name + ")))";
                search.PropertiesToLoad.Add("cn");
                search.PropertiesToLoad.Add("samaccountname");
                search.PropertiesToLoad.Add("memberOf");
                sr1 = search.FindOne();
                GetUserGroupsRecursive(groups, sr1, de);
            }
        }
    }

这是我列出特定专有名称的所有组(直接和间接)的方式:

字符串1.2.840.113556.1.4.1941指定 LDAP_MATCHING_RULE_IN_CHAIN。

此规则仅限于应用于 DN 的过滤器。 这是一个特殊的“扩展”匹配运算符,它沿着对象中的祖先链一直走到根,直到找到匹配项。

在我的测试中,此方法UserPrincipal.GetGroups()方法快 25 倍

注意:此方法或 GetGroups() 方法不返回主要组(通常是域用户)。 为了获得主要组名,我已经确认此方法有效。

此外,我发现这个LDAP过滤器列表非常有用。

private IEnumerable<string> GetGroupsForDistinguishedName(DirectoryEntry domainDirectoryEntry, string distinguishedName)
{
    var groups = new List<string>();
    if (!string.IsNullOrEmpty(distinguishedName))
    {
        var getGroupsFilterForDn = $"(&(objectCategory=group)(member:1.2.840.113556.1.4.1941:={distinguishedName}))";
        using (DirectorySearcher dirSearch = new DirectorySearcher(domainDirectoryEntry))
        {
            dirSearch.Filter = getGroupsFilterForDn;
            dirSearch.PropertiesToLoad.Add("name");

            using (var results = dirSearch.FindAll())
            {
                foreach (SearchResult result in results)
                {
                    if (result.Properties.Contains("name"))
                        groups.Add((string)result.Properties["name"][0]);
                }
            }
        }
    }

    return groups;
}

这是对我有用的代码:

public ArrayList GetBBGroups(WindowsIdentity identity)
{
    ArrayList groups = new ArrayList();

    try
    {
        String userName = identity.Name;
        int pos = userName.IndexOf(@"\");
        if (pos > 0) userName = userName.Substring(pos + 1);

        PrincipalContext domain = new PrincipalContext(ContextType.Domain, "riomc.com");
        UserPrincipal user = UserPrincipal.FindByIdentity(domain, IdentityType.SamAccountName, userName);

        DirectoryEntry de = new DirectoryEntry("LDAP://RIOMC.com");
        DirectorySearcher search = new DirectorySearcher(de);
        search.Filter = "(&(objectClass=group)(member=" + user.DistinguishedName + "))";
        search.PropertiesToLoad.Add("samaccountname");
        search.PropertiesToLoad.Add("cn");

        String name;
        SearchResultCollection results = search.FindAll();
        foreach (SearchResult result in results)
        {
            name = (String)result.Properties["samaccountname"][0];
            if (String.IsNullOrEmpty(name))
            {
                name = (String)result.Properties["cn"][0];
            }
            GetGroupsRecursive(groups, de, name);
        }
    }
    catch
    {
        // return an empty list...
    }

    return groups;
}

public void GetGroupsRecursive(ArrayList groups, DirectoryEntry de, String dn)
{
    DirectorySearcher search = new DirectorySearcher(de);
    search.Filter = "(&(objectClass=group)(|(samaccountname=" + dn + ")(cn=" + dn + ")))";
    search.PropertiesToLoad.Add("memberof");

    String group, name;
    SearchResult result = search.FindOne();
    if (result == null) return;

    group = @"RIOMC\" + dn;
    if (!groups.Contains(group))
    {
        groups.Add(group);
    }
    if (result.Properties["memberof"].Count == 0) return;
    int equalsIndex, commaIndex;
    foreach (String dn1 in result.Properties["memberof"])
    {
        equalsIndex = dn1.IndexOf("=", 1);
        if (equalsIndex > 0)
        {
            commaIndex = dn1.IndexOf(",", equalsIndex + 1);
            name = dn1.Substring(equalsIndex + 1, commaIndex - equalsIndex - 1);
            GetGroupsRecursive(groups, de, name);
        }
    }
}

我针对使用AttributeValuesMultiString递归方法的代码在 200 次循环中测量了它的性能; 它的工作速度提高了 1.3 倍。 可能是因为我们的 AD 设置。 两个片段都给出了相同的结果。

以下示例来自 Code Project 文章, (Almost) Everything In Active Directory via C#

// userDn is a Distinguished Name such as:
// "LDAP://CN=Joe Smith,OU=Sales,OU=domain,OU=com"
public ArrayList Groups(string userDn, bool recursive)
{
    ArrayList groupMemberships = new ArrayList();
    return AttributeValuesMultiString("memberOf", userDn,
        groupMemberships, recursive);
}

public ArrayList AttributeValuesMultiString(string attributeName,
     string objectDn, ArrayList valuesCollection, bool recursive)
{
    DirectoryEntry ent = new DirectoryEntry(objectDn);
    PropertyValueCollection ValueCollection = ent.Properties[attributeName];
    IEnumerator en = ValueCollection.GetEnumerator();

    while (en.MoveNext())
    {
        if (en.Current != null)
        {
            if (!valuesCollection.Contains(en.Current.ToString()))
            {
                valuesCollection.Add(en.Current.ToString());
                if (recursive)
                {
                    AttributeValuesMultiString(attributeName, "LDAP://" +
                    en.Current.ToString(), valuesCollection, true);
                }
            }
        }
    }
    ent.Close();
    ent.Dispose();
    return valuesCollection;
}

只需使用用户的专有名称调用Groups方法,并传入 bool 标志以指示是否要在生成的 ArrayList 中包含嵌套/子组成员身份:

ArrayList groups = Groups("LDAP://CN=Joe Smith,OU=Sales,OU=domain,OU=com", true);
foreach (string groupName in groups)
{
    Console.WriteLine(groupName);
}

如果您需要在 .NET 中进行任何认真级别的 Active Directory 编程,我强烈建议您添加书签并查看我上面提到的代码项目文章。

我想说的是,Microsoft LDAP 有一些特殊的方法来递归搜索用户的所有成员资格。

  1. 您可以为“成员”属性指定的匹配规则。 特别是,对“成员”属性使用 Microsoft 独占LDAP_MATCHING_RULE_IN_CHAIN规则允许递归/嵌套成员资格搜索。 在成员属性之后添加规则时会使用该规则。 例如。 (会员:1.2.840.113556.1.4.1941:= XXXX)

  2. 对于与帐户相同的域,过滤器可以使用 <SID=S-1-5-21-XXXXXXXXXXXXXXXXXXXXXXXXX> 而不是 Accounts DistinguishedName 属性,这对于在需要时使用跨域非常方便。 但是,您似乎需要使用 ForeignSecurityPrincipal <GUID=YYYY>,因为它不会解析您的 SID,因为 <SID=> 标记似乎不考虑 ForeignSecurityPrincipal 对象类型。 您也可以使用 ForeignSecurityPrincipal DistinguishedName。

使用这些知识,您可以通过 LDAP 查询那些难以获得成员资格的成员,例如帐户所属的“本地域”组,但除非您查看该组的成员,否则您不会知道用户是否是成员。

//Get Direct+Indirect Memberships of User (where SID is XXXXXX)

string str = "(& (objectCategory=group)(member:1.2.840.113556.1.4.1941:=<SID=XXXXXX>) )";

//Get Direct+Indirect **Domain Local** Memberships of User (where SID is XXXXXX)

string str2 = "(& (objectCategory=group)(|(groupType=-2147483644)(groupType=4))(member:1.2.840.113556.1.4.1941:=<SID=XXXXXX>) )";

//TAA DAA



在替换您要检索其所有组成员身份的用户的 SID 后,您可以随意尝试这些 LDAP 查询。 我认为这与PowerShell 命令 Get-ADPrincipalGroupMembership在幕后使用的查询很相似,如果不是相同的话。 该命令指出“如果要搜索另一个域中的本地组,请使用 ResourceContextServer 参数指定另一个域中的备用服务器。”

如果您对 C# 和 Active Directory 足够熟悉,您应该知道如何使用提供的 LDAP 查询执行 LDAP 搜索。

附加文档:

如果你有一个带有用户名和密码的 LDAP 连接来连接到 Active Directory,这里是我用来正确连接的代码:

using System.DirectoryServices.AccountManagement;

// ...

// Connection information
var connectionString = "LDAP://domain.com/DC=domain,DC=com";
var connectionUsername = "your_ad_username";
var connectionPassword = "your_ad_password";

// Get groups for this user
var username = "myusername";

// Split the LDAP Uri
var uri = new Uri(connectionString);
var host = uri.Host;
var container = uri.Segments.Count() >=1 ? uri.Segments[1] : "";

// Create context to connect to AD
var princContext = new PrincipalContext(ContextType.Domain, host, container, connectionUsername, connectionPassword);

// Get User
UserPrincipal user = UserPrincipal.FindByIdentity(princContext, IdentityType.SamAccountName, username);

// Browse user's groups
foreach (GroupPrincipal group in user.GetGroups())
{
    Console.Out.WriteLine(group.Name);
}

有一个助手 class 基于 curtisk 响应:

    public static class ActiveDirectoryHelpers
    {
        private static readonly Regex keyValuePair = new Regex($"(?<key>[^=]+)=(?<value>[^,]+),?");

        public enum X500DirectorySpecification
        {
            /// <summary>Common Name</summary>
            CN,
            /// <summary>Organizational Unit</summary>
            OU,
            /// <summary>Domain Component</summary>
            DC
        }

        public static IEnumerable<string> GetUserMemberOfNodeValue(this PrincipalContext principalContext, string userName, X500DirectorySpecification node)
        {
            return principalContext.GetUserMemberOf(userName)
                .SelectMany(memberOf =>
                    GetUserMemberOfKeyValues(memberOf).Where(item => item.Key == node.ToString()).Select(item => item.Value));
        }

        private static IEnumerable<string> GetUserMemberOf(this PrincipalContext principalContext, string userName)
        {
            using var user = UserPrincipal.FindByIdentity(principalContext, userName);
            IEnumerable<string> result = null;
            if (user != null)
            {
                var directoryEntry = (DirectoryEntry)user.GetUnderlyingObject();
                var directorySearcher = new DirectorySearcher(directoryEntry);

                directorySearcher.PropertiesToLoad.Add("memberOf");
                result = directorySearcher.FindOne().Properties["memberOf"].Cast<string>();
            }

            return result ?? Enumerable.Empty<string>();
        }

        private static IEnumerable<KeyValuePair<string, string>> GetUserMemberOfKeyValues(string memberOfValue)
        {
            return keyValuePair.Matches(memberOfValue).OfType<Match>()
                .Select(item => new KeyValuePair<string, string>(item.Groups["key"].Value.Trim(), item.Groups["value"].Value));
        }
    }
PrincipalContext pc1 = new PrincipalContext(ContextType.Domain, "DomainName", UserAccountOU, UserName, Password);
UserPrincipal UserPrincipalID = UserPrincipal.FindByIdentity(pc1, IdentityType.SamAccountName, UserID);

searcher.Filter = "(&(ObjectClass=group)(member = " + UserPrincipalID.DistinguishedName + "));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM