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

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

正如我们的Windows服务轮询AD,给定一个LDAP(即LDAP://10.32.16.80)和该AD服务器中要搜索的用户组列表。 它检索这些给定组中的所有用户,以递归方式搜索这些组以获取更多组。 然后将每个用户添加到另一个应用程序认证用户列表中。

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

因此,如果有一个用户属于TEST域,则名为Steve:TEST / steve是他的登录名。 我能够在AD中找到steve,但是我还需要“TEST”与他的AD信息一起存储。

再一次,我可以通过使用目录搜索器和我给出的LDAP IP找到’史蒂夫’,但是考虑到LDAP IP,我怎样才能找到友好的域名?

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

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

这是代码:

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()”调用时出现exception:“未指定的错误”。 传入的字符串是:“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; 

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

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

编辑#1

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

 private IEnumerable GetDomains() { ICollection domains = new List(); // 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 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. 将它连接到反斜杠(****)和找到的登录名。

下面的示例使用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()) } 

我启发自己的来源是:
在AD中查找域的NetBios名称

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

 using System.DirectoryServices; public static class Methods { public static T ldap_get_value(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(parent.Properties["objectClass"]) == "domainDNS") return ldap_get_value(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(user.Properties["displayName"])); Console.WriteLine("Domain: " + Methods.ldap_get_domainname(user)); Console.WriteLine("Login: " + Methods.ldap_get_domainname(user) + "\\" + Methods.ldap_get_value(user.Properties["samAccountName"])); } } 

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

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

 string domainName; domainName = System.Environment.UserDomainName; 

也许不完全正确,但……

 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 = FullName ,DC = ,DC =本地