通过sAMAccountName和域查询活动目录LDAP

28

如何通过sAMAccountName和Domain查询LDAP存储?在Active Directory或LDAP术语中,“domain”属性的名称是什么?

目前,我已经有了以下筛选器。我希望能够添加域:

(&(objectCategory=Person)(sAMAccountName=BTYNDALL))
7个回答

28

首先,修改您的搜索过滤器,只搜索用户而不是联系人:

(&(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

2
'With'和'While'语句看起来很丑。我想我很久以前写的,自从它正常工作以来就没有更新过... - Dscoduc
+1和答案。这正是我所期望的思维方式。谢谢。 - BuddyJoe

17

您可以使用以下查询

Logon Name(Pre-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))

我该如何搜索多个sAMAccountName? - Bharat Banavalikar
谢谢!为了让它在我的应用程序中正常工作,我不得不将!后面的任何内容用括号括起来:...(!(sAMAccountType=805306370))... - Brad Turek

11

搜索用户的最佳方式是:(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上,无法通过部分匹配进行搜索。


5
“域”不是LDAP对象的属性。它更像是对象存储在其中的“数据库”的名称。
因此,您必须连接到正确的数据库(在LDAP术语中:绑定到域/目录服务器),才能在该数据库中执行搜索。
一旦成功绑定,您当前查询的形式就是您所需的全部内容。
顺便说一句:选择“ObjectCategory=Person”而不是“ObjectClass=user”是一个很好的决定。在AD中,前者是具有出色性能的“索引属性”,而后者没有索引且稍微慢一些。

2
在AD中查询用户时,我喜欢使用sAMAccountType=805306368,因为这会限制你的搜索范围并且速度快。 - benPearce

3

3

如果你正在使用.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 + ")";

1
假设我的登录名是COMPANY\BTYNDALL,我该如何假定LDAP字符串将是LDAP://DC=company,DC=com?因为在我的情况下,这是错误的,最后一个DC应该是DC=net。是否有一种方法可以在AD中查找“短域”,并获取更长的LDAP字符串呢? - BuddyJoe
我是否需要在我的应用程序中构建一个查找表,还是只需构建一个查找表? - BuddyJoe

2
我已经编写了一个C#类,其中包括以下内容:
  • Dscoduc的算法
  • sorin的查询优化
  • 用于域到服务器映射的缓存
  • 以及在DOMAIN\sAMAccountName格式中搜索帐户名的方法。
不过,它并不具备站点感知功能。
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://dev59.com/wHRB5IYBdhLWcg3wXWG2
    /// </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();
    }
}

哇,这是一个隐藏的宝藏。我意识到@Dscoduc应该得到赞扬,但我没有耐心去翻译那个Visual Basic。 - mdisibio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接