C#Active Directory:获取用户的域名?

时间:2010-11-22 18:58:54

标签: c# active-directory dns comexception

我知道之前已经问过这类问题,但其他方法现在都让我失望了。

现在我们的Windows服务轮询AD,给定一个LDAP(即LDAP://10.32.16.80)以及该AD服务器中要搜索的用户组列表。 它检索这些给定组中的所有用户,以递归方式搜索这些组以获取更多组。 然后将每个用户添加到另一个经过身份验证的用户列表中。

此部分应用程序已成功运行。但是,我们需要每个用户的友好域名(即他们登录DOMAIN /用户名的一部分)

因此,如果有一个用户是TEST域的一部分,名为Steve:TEST / steve就是他的登录名。 我能够在广告中找到史蒂夫,但是我还需要将“TEST”与他的AD信息一起存储。

同样,我可以通过使用目录搜索器和我给出的LDAP IP找到'史蒂夫',但是如果给出了LDAP IP,我该如何找到友好的域名?

当我尝试以下代码时,我在尝试访问'defaultNamingContext'时遇到错误:

System.Runtime.InteropServices.COMException(0x8007202A):身份验证机制未知。

以下是代码:

    private string SetCurrentDomain(string server)
    {
        string result = string.Empty;
        try
        {
            logger.Debug("'SetCurrentDomain'; Instantiating rootDSE LDAP");
            DirectoryEntry ldapRoot = new DirectoryEntry(server + "/rootDSE", username, password);
            logger.Debug("'SetCurrentDomain'; Successfully instantiated rootDSE LDAP");

            logger.Debug("Attempting to retrieve 'defaultNamingContext'...");
            string domain = (string)ldapRoot.Properties["defaultNamingContext"][0]; //THIS IS WHERE I HIT THE COMEXCEPTION
            logger.Debug("Retrieved 'defaultNamingContext': " + domain);
            if (!domain.IsEmpty())
            {

                logger.Debug("'SetCurrentDomain'; Instantiating partitions/configuration LDAP entry");
                DirectoryEntry parts = new DirectoryEntry(server + "/CN=Partitions,CN=Configuration," + domain, username, password);

                logger.Debug("'SetCurrentDomain'; Successfully instantiated partitions/configuration LDAP entry");
                foreach (DirectoryEntry part in parts.Children)
                {
                    if (part.Properties["nCName"] != null && (string)part.Properties["nCName"][0] != null)
                    {
                        logger.Debug("'SetCurrentDomain'; Found property nCName");
                        if ((string)part.Properties["nCName"][0] == domain)
                        {
                            logger.Debug("'SetCurrentDomain'; nCName matched defaultnamingcontext");
                            result = (string)part.Properties["NetBIOSName"][0];
                            logger.Debug("'SetCurrentDomain'; Found NetBIOSName (friendly domain name): " + result);
                            break;
                        }
                    }
                }
            }
            logger.Debug("finished setting current domain...");
        }
        catch (Exception ex)
        {
            logger.Error("error attempting to set domain:" + ex.ToString());
        }
        return result;
    }

修改

我添加了这个示例方法以尝试建议,但是当我在搜索器上点击“FindAll()”调用时出现异常:“未指定的错误”。 传入的字符串是:“CN = TEST USER,CN = Users,DC = tempe,DC = ktregression,DC = com”

        private string GetUserDomain(string dn)
    {
        string domain = string.Empty;
        string firstPart = dn.Substring(dn.IndexOf("DC="));
        string secondPart = "CN=Partitions,CN=Configuration," + firstPart;
        DirectoryEntry root = new DirectoryEntry(secondPart, textBox2.Text, textBox3.Text);
        DirectorySearcher searcher = new DirectorySearcher(root);
        searcher.SearchScope = SearchScope.Subtree;
        searcher.ReferralChasing = ReferralChasingOption.All;
        searcher.Filter = "(&(nCName=" + firstPart + ")(nETBIOSName=*))";
        try
        {
            SearchResultCollection rs = searcher.FindAll();
            if (rs != null)
            {
                domain = GetProperty(rs[0], "nETBIOSName");
            }
        }
        catch (Exception ex)
        {

        }


        return domain;

4 个答案:

答案 0 :(得分:27)

本文帮助我了解了如何使用Active Directory Howto: (Almost) Everything In Active Directory via C#

从现在开始,如果您需要进一步的协助,请在评论中告知我正确的问题,我将尽我所知为您解答。

编辑#1

您最好使用此示例的过滤器。我编写了一些示例代码,以简要说明如何使用System.DirectoryServicesSystem.DirectoryServices.ActiveDirectory命名空间。 System.DirectoryServices.ActiveDirectory 命名空间用于检索有关林中域的信息。

private IEnumerable<DirectoryEntry> GetDomains() {
    ICollection<string> domains = new List<string>();

    // Querying the current Forest for the domains within.
    foreach(Domain d in Forest.GetCurrentForest().Domains)
        domains.Add(d.Name);

    return domains;
}

private string GetDomainFullName(string friendlyName) {
    DirectoryContext context = new DirectoryContext(DirectoryContextType.Domain, friendlyName);
    Domain domain = Domain.GetDomain(context);
    return domain.Name;
}

private IEnumerable<string> GetUserDomain(string userName) {
    foreach(string d in GetDomains()) 
        // From the domains obtained from the Forest, we search the domain subtree for the given userName.
        using (DirectoryEntry domain = new DirectoryEntry(GetDomainFullName(d))) {
            using (DirectorySearcher searcher = new DirectorySearcher()){
                searcher.SearchRoot = domain;
                searcher.SearchScope = SearchScope.Subtree;
                searcher.PropertiesToLoad.Add("sAMAccountName");
                // The Filter is very important, so is its query string. The 'objectClass' parameter is mandatory.
                // Once we specified the 'objectClass', we want to look for the user whose login
                // login is userName.
                searcher.Filter = string.Format("(&(objectClass=user)(sAMAccountName={0}))", userName);

                try {
                    SearchResultCollection  results = searcher.FindAll();

                    // If the user cannot be found, then let's check next domain.
                    if (results == null || results.Count = 0)
                        continue;

                     // Here, we yield return for we want all of the domain which this userName is authenticated.
                     yield return domain.Path;
                } finally {
                    searcher.Dispose();
                    domain.Dispose();
                }
            }
}

在这里,我没有测试此代码,可能还有一些小问题需要解决。此示例按原样提供,以帮助您。我希望这会有所帮助。

编辑#2

我找到了另一条出路:

  1. 您首先要查看是否可以在您的域中找到用户帐户;
  2. 如果找到,则获取域NetBIOS名称;和
  3. 将其连接到反斜杠(****)和找到的登录名。
  4. 以下示例使用 NUnit TestCase ,您可以自己测试,看看它是否符合您的要求。

    [TestCase("LDAP://fully.qualified.domain.name", "TestUser1")] 
    public void GetNetBiosName(string ldapUrl, string login)
        string netBiosName = null;
        string foundLogin = null;
    
        using (DirectoryEntry root = new DirectoryEntry(ldapUrl))
            Using (DirectorySearcher searcher = new DirectorySearcher(root) {
                searcher.SearchScope = SearchScope.Subtree;
                searcher.PropertiesToLoad.Add("sAMAccountName");
                searcher.Filter = string.Format("(&(objectClass=user)(sAMAccountName={0}))", login);
    
                SearchResult result = null;
    
                try {
                    result = searcher.FindOne();
    
                    if (result == null) 
                        if (string.Equals(login, result.GetDirectoryEntry().Properties("sAMAccountName").Value)) 
                            foundLogin = result.GetDirectoryEntry().Properties("sAMAccountName").Value
                } finally {
                    searcher.Dispose();
                    root.Dispose();
                    if (result != null) result = null;
                }
            }
    
        if (!string.IsNullOrEmpty(foundLogin)) 
            using (DirectoryEntry root = new DirectoryEntry(ldapUrl.Insert(7, "CN=Partitions,CN=Configuration,DC=").Replace(".", ",DC=")) 
                Using DirectorySearcher searcher = new DirectorySearcher(root)
                    searcher.Filter = "nETBIOSName=*";
                    searcher.PropertiesToLoad.Add("cn");
    
                    SearchResultCollection results = null;
    
                    try {
                        results = searcher.FindAll();
    
                        if (results != null && results.Count > 0 && results[0] != null) {
                            ResultPropertyValueCollection values = results[0].Properties("cn");
                            netBiosName = rpvc[0].ToString();
                    } finally {
                        searcher.Dispose();
                        root.Dispose();
    
                        if (results != null) {
                            results.Dispose();
                            results = null;
                        }
                    }
                }
    
        Assert.AreEqual("FULLY\TESTUSER1", string.Concat(netBiosName, "\", foundLogin).ToUpperInvariant())
    }
    

    我启发自己的来源是:
    Find the NetBios Name of a domain in AD

答案 1 :(得分:5)

由于我找不到任何示例代码,我想分享自己的解决方案。这将搜索DirectoryEntry对象的父级,直到它到达domainDNS类。

using System.DirectoryServices;

public static class Methods
{
    public static T ldap_get_value<T>(PropertyValueCollection property)
    {
        object value = null;
        foreach (object tmpValue in property) value = tmpValue;
        return (T)value;
    }

    public static string ldap_get_domainname(DirectoryEntry entry)
    {
        if (entry == null || entry.Parent == null) return null;
        using (DirectoryEntry parent = entry.Parent)
        {
            if (ldap_get_value<string>(parent.Properties["objectClass"]) == "domainDNS") 
                return ldap_get_value<string>(parent.Properties["dc"]);
            else 
                return ldap_get_domainname(parent);
        }
    }
}

像这样使用:

string[] _properties = new string[] { "objectClass", "distinguishedName", "samAccountName", "userPrincipalName", "displayName", "mail", "title", "company", "thumbnailPhoto", "useraccountcontrol" };
string account = "my-user-name";
// OR even better:
// string account = "my-user-name@DOMAIN.local";

using (DirectoryEntry ldap = new DirectoryEntry())
{
    using (DirectorySearcher searcher = new DirectorySearcher(ldap))
    {
        searcher.PropertiesToLoad.AddRange(_properties);
        if (account.Contains('@')) searcher.Filter = "(userPrincipalName=" + account + ")";
        else searcher.Filter = "(samAccountName=" + account + ")";
        var user = searcher.FindOne().GetDirectoryEntry();

        Console.WriteLine("Name: " + Methods.ldap_get_value<string>(user.Properties["displayName"]));
        Console.WriteLine("Domain: " + Methods.ldap_get_domainname(user));
        Console.WriteLine("Login: " + Methods.ldap_get_domainname(user) + "\\" + Methods.ldap_get_value<string>(user.Properties["samAccountName"]));
    }
}

我没有森​​林来测试它,但理论上这应该会削减它。

答案 2 :(得分:3)

您可以使用Environment.UserDomainName Property检索当前用户所在域的名称。

string domainName;
domainName = System.Environment.UserDomainName;

答案 3 :(得分:1)

也许不完全正确,但......

DirectoryEntry dirEntry = new DirectoryEntry();         
DirectorySearcher dirSearcher = new DirectorySearcher(dirEntry);
dirSearcher.SearchScope = SearchScope.Subtree;
dirSearcher.Filter = string.Format("(&(objectClass=user)(|(cn={0})(sn={0}*)(givenName={0})(sAMAccountName={0}*)))", userName);
var searchResults = dirSearcher.FindAll();

foreach (SearchResult sr in searchResults)
{
     var de = sr.GetDirectoryEntry();
     string user = de.Properties["SAMAccountName"][0].ToString();               
     string domain = de.Path.ToString().Split(new [] { ",DC=" },StringSplitOptions.None)[1];
     MessageBox.Show(domain + "/" + user);
}

因为de.Path的值是

  

LDAP:// CN = 全名,DC = ,DC =本地