按 sAMAccountName 和域进行 Active Directory LDAP 查询

发布于 2024-07-12 08:32:27 字数 206 浏览 12 评论 0原文

如何通过 sAMAccountName 和域查询 LDAP 存储? Active Directory 或 LDAP 术语中命名的“域”属性是什么?

这就是我到目前为止所拥有的过滤器。 我希望能够在域中添加:

(&(objectCategory=Person)(sAMAccountName=BTYNDALL))

How do you do a query of an LDAP store by sAMAccountName and Domain? What is the "domain" property named in Active Directory or LDAP terms?

This is what I have for the filter so far. I'd like to be able to add in the domain:

(&(objectCategory=Person)(sAMAccountName=BTYNDALL))

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

不乱于心 2024-07-19 08:32:27

首先,修改搜索过滤器以仅查找用户而不查找联系人:

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

First, modify your search filter to only look for users and not contacts:

(&(objectCategory=person)(objectClass=user)(sAMAccountName=BTYNDALL))

You can enumerate all of the domains of a forest by connecting to the configuration partition and enumerating all the entries in the partitions container. Sorry I don't have any C# code right now but here is some vbscript code I've used in the past:

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

From that you can retrieve the name and dnsRoot of each partition:

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
简单爱 2024-07-19 08:32:27

您可以使用以下查询

登录名(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))

You can use following queries

Users whose Logon Name(Pre-Windows 2000) is equal to John

(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(sAMAccountName=**John**))

All Users

(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370))

Enabled Users

(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(!userAccountControl:1.2.840.113556.1.4.803:=2))

Disabled Users

(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(userAccountControl:1.2.840.113556.1.4.803:=2))

LockedOut Users

(&(objectCategory=person)(objectClass=user)(!sAMAccountType=805306370)(lockouttime>=1))
方觉久 2024-07-19 08:32:27

搜索用户最佳方式(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 查询的资源 - 尝试自己找到它们你会浪费宝贵的时间,而且肯定会犯错误。

关于域:在单个查询中这是不可能的,因为域是用户 distinguishedName (DN) 的一部分,在 Microsoft AD 上,无法通过部分匹配进行搜索。

The best way of searching for users is (sAMAccountType=805306368).

Or for disabled users:

(&(sAMAccountType=805306368)(userAccountControl:1.2.840.113556.1.4.803:=2))

Or for active users:

(&(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))

I find LDAP as not being so light at it was supposed to be.

Also resource for common LDAP queries - trying to find them yourself and you will lose precious time and definitely make mistakes.

Regarding domains: it's not possible in a single query because the domain is part of the user distinguisedName (DN) which, on Microsoft AD, is not searchable by partial matching.

忆伤 2024-07-19 08:32:27

“域”不是 LDAP 对象的属性。 它更像是存储对象的数据库的名称。

因此您必须连接到正确的数据库(用LDAP术语来说:“绑定到域/目录服务器”) 以便在该数据库中执行搜索。

绑定成功后,当前形状的查询就足够了。

顺便说一句:选择 “ObjectCategory=Person” 而不是 “ObjectClass=user” 是一个不错的决定。 在AD中,前者是“索引属性”,性能优良,后者没有索引,速度稍慢。

"Domain" is not a property of an LDAP object. It is more like the name of the database the object is stored in.

So you have to connect to the right database (in LDAP terms: "bind to the domain/directory server") in order to perform a search in that database.

Once you bound successfully, your query in it's current shape is all you need.

BTW: Choosing "ObjectCategory=Person" over "ObjectClass=user" was a good decision. In AD, the former is an "indexed property" with excellent performance, the latter is not indexed and a tad slower.

傾城如夢未必闌珊 2024-07-19 08:32:27

您必须在域中执行搜索:

http:// /msdn.microsoft.com/en-us/library/ms677934(VS.85).aspx
因此,基本上您应该绑定到一个域才能在该域内进行搜索。

You have to perform your search in the domain:

http://msdn.microsoft.com/en-us/library/ms677934(VS.85).aspx
So, basically your should bind to a domain in order to search inside this domain.

如果您使用的是 .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 + ")";

If you're using .NET, use the DirectorySearcher class. You can pass in your domain as a string into the constructor.

// 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 + ")";
淡笑忘祈一世凡恋 2024-07-19 08:32:27

我编写了一个 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://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();
    }
}

I have written a C# class incorporating

  • the algorithm from Dscoduc,
  • the query optimization from sorin,
  • a cache for the domain to server mapping, and
  • a method to search for an account name in DOMAIN\sAMAccountName format.

However, it is not Site-aware.

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 => 
quot;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(
quot;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,
                
quot;(&(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(
quot;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();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文