如何通过 sAMAccountName 和域查询 LDAP 存储?Active Directory 或 LDAP 术语中命名的“域”属性是什么?
到目前为止,这就是我所拥有的过滤器。我希望能够在域中添加:
(&(objectCategory=Person)(sAMAccountName=BTYNDALL))
如何通过 sAMAccountName 和域查询 LDAP 存储?Active Directory 或 LDAP 术语中命名的“域”属性是什么?
到目前为止,这就是我所拥有的过滤器。我希望能够在域中添加:
(&(objectCategory=Person)(sAMAccountName=BTYNDALL))
首先,修改您的搜索过滤器以仅查找用户而不是联系人:
(&(objectCategory=person)(objectClass=user)(sAMAccountName=BTYNDALL))
您可以通过连接到配置分区并枚举分区容器中的所有条目来枚举森林的所有域。抱歉,我现在没有任何 C# 代码,但这里有一些我过去使用过的 vbscript 代码:
Set objRootDSE = GetObject("LDAP://RootDSE")
AdComm.Properties("Sort on") = "name"
AdComm.CommandText = "<LDAP://cn=Partitions," & _
objRootDSE.Get("ConfigurationNamingContext") & ">;" & _
"(&(objectcategory=crossRef)(systemFlags=3));" & _
"name,nCName,dnsRoot;onelevel"
set AdRs = AdComm.Execute
从中您可以检索每个分区的名称和 dnsRoot:
AdRs.MoveFirst
With AdRs
While Not .EOF
dnsRoot = .Fields("dnsRoot")
Set objOption = Document.createElement("OPTION")
objOption.Text = dnsRoot(0)
objOption.Value = "LDAP://" & dnsRoot(0) & "/" & .Fields("nCName").Value
Domain.Add(objOption)
.MoveNext
Wend
End With
您可以使用以下查询
登录名(Windows 2000 之前)等于John的用户
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(sAMAccountName=**John**))
所有用户
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370))
启用的用户
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(!userAccountControl:1.2.840.113556.1.4.803:=2))
禁用用户
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(userAccountControl:1.2.840.113556.1.4.803:=2))
锁定用户
(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(lockouttime>=1))
搜索用户的最佳方式是.(sAMAccountType=805306368)
或者对于残疾用户:
(&(sAMAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=2))
或者对于活跃用户:
(&(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))
我发现 LDAP 并不像它应该的那样轻巧。
也是常见 LDAP 查询的资源- 尝试自己找到它们,您将失去宝贵的时间并且肯定会犯错误。
关于域:在单个查询中是不可能的,因为域是用户distinguisedName
( DN
) 的一部分,在 Microsoft AD 上,不能通过部分匹配进行搜索。
“域”不是 LDAP 对象的属性。它更像是存储对象的数据库的名称。
因此,您必须连接到正确的数据库(在 LDAP 术语中:“绑定到域/目录服务器”)才能在该数据库中执行搜索。
成功绑定后,您只需要当前形状的查询即可。
顺便说一句:选择是一个很好"ObjectCategory=Person"
的"ObjectClass=user"
决定。在 AD 中,前者是一个性能优异的“索引属性”,后者没有索引并且速度稍慢。
您必须在域中执行搜索:
http://msdn.microsoft.com/en-us/library/ms677934(VS.85).aspx 所以,基本上你应该绑定到一个域以便在这个域内搜索。
如果您使用的是 .NET,请使用DirectorySearcher类。您可以将域作为字符串传递给构造函数。
// if you domain is domain.com...
string username = "user"
string domain = "LDAP://DC=domain,DC=com";
DirectorySearcher search = new DirectorySearcher(domain);
search.Filter = "(SAMAccountName=" + username + ")";
我写了一个 C# 类
但是,它不是站点感知的。
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Linq;
using System.Text;
public static class ADUserFinder
{
private static Dictionary<string, string> _dictDomain2LDAPPath;
private static Dictionary<string, string> DictDomain2LDAPPath
{
get
{
if (null == _dictDomain2LDAPPath)
{
string configContainer;
using (DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE"))
configContainer = rootDSE.Properties["ConfigurationNamingContext"].Value.ToString();
using (DirectoryEntry partitionsContainer = new DirectoryEntry("LDAP://CN=Partitions," + configContainer))
using (DirectorySearcher dsPartitions = new DirectorySearcher(
partitionsContainer,
"(&(objectcategory=crossRef)(systemFlags=3))",
new string[] { "name", "nCName", "dnsRoot" },
SearchScope.OneLevel
))
using (SearchResultCollection srcPartitions = dsPartitions.FindAll())
{
_dictDomain2LDAPPath = srcPartitions.OfType<SearchResult>()
.ToDictionary(
result => result.Properties["name"][0].ToString(), // the DOMAIN part
result => $"LDAP://{result.Properties["dnsRoot"][0]}/{result.Properties["nCName"][0]}"
);
}
}
return _dictDomain2LDAPPath;
}
}
private static DirectoryEntry FindRootEntry(string domainPart)
{
if (DictDomain2LDAPPath.ContainsKey(domainPart))
return new DirectoryEntry(DictDomain2LDAPPath[domainPart]);
else
throw new ArgumentException($"Domain \"{domainPart}\" is unknown in Active Directory");
}
public static DirectoryEntry FindUser(string domain, string sAMAccountName)
{
using (DirectoryEntry rootEntryForDomain = FindRootEntry(domain))
using (DirectorySearcher dsUser = new DirectorySearcher(
rootEntryForDomain,
$"(&(sAMAccountType=805306368)(sAMAccountName={EscapeLdapSearchFilter(sAMAccountName)}))" // magic number 805306368 means "user objects", it's more efficient than (objectClass=user)
))
return dsUser.FindOne().GetDirectoryEntry();
}
public static DirectoryEntry FindUser(string domainBackslashSAMAccountName)
{
string[] domainAndsAMAccountName = domainBackslashSAMAccountName.Split('\\');
if (domainAndsAMAccountName.Length != 2)
throw new ArgumentException($"User name \"{domainBackslashSAMAccountName}\" is not in correct format DOMAIN\\SAMACCOUNTNAME", "DomainBackslashSAMAccountName");
string domain = domainAndsAMAccountName[0];
string sAMAccountName = domainAndsAMAccountName[1];
return FindUser(domain, sAMAccountName);
}
/// <summary>
/// Escapes the LDAP search filter to prevent LDAP injection attacks.
/// Copied from https://stackoverflow.com/questions/649149/how-to-escape-a-string-in-c-for-use-in-an-ldap-query
/// </summary>
/// <param name="searchFilter">The search filter.</param>
/// <see cref="https://blogs.oracle.com/shankar/entry/what_is_ldap_injection" />
/// <see cref="http://msdn.microsoft.com/en-us/library/aa746475.aspx" />
/// <returns>The escaped search filter.</returns>
private static string EscapeLdapSearchFilter(string searchFilter)
{
StringBuilder escape = new StringBuilder();
for (int i = 0; i < searchFilter.Length; ++i)
{
char current = searchFilter[i];
switch (current)
{
case '\\':
escape.Append(@"\5c");
break;
case '*':
escape.Append(@"\2a");
break;
case '(':
escape.Append(@"\28");
break;
case ')':
escape.Append(@"\29");
break;
case '\u0000':
escape.Append(@"\00");
break;
case '/':
escape.Append(@"\2f");
break;
default:
escape.Append(current);
break;
}
}
return escape.ToString();
}
}