通过ADSI 对Active Directory操作

使用 System.DirectoryServices 搜索 Active Directory

简介

Active Directory 是一种具有特殊用途的数据库,它可以在整个组织范围内复制并轻松扩展,从而更好地存储用户信息、网络配置以及其他在组织范围内进行全局访问所需的数据。它是最强大的操作系统功能之一,如果您的组织拥有 Active Directory,则非常值得在构建自己的应用程序时加以利用。

在本文中,我将详细介绍如何使用 System.DirectoryServices 命名空间来连接 Active Directory、搜索对象并显示搜索结果。当然,System.DirectoryServices 不仅适用于 Active Directory,它还可以用于几种不同的服务,包括普遍使用的 LDAP 协议,但这里主要针对 Active Directory 介绍我的示例。

[@more@]

ADSI 转至 System.DirectoryServices

刚开始使用 Active Directory 进行编程的人员可能会对 MSDN 和其他资源上提到的 ADSI 存有疑问。ADSI(即 Active Directory Service InterfacesActive Directory 服务接口)是一种 COM 库,它使您可以实现非 .NET 语言与 Active Directory 的交互。它是使用 Microsoft® Visual Basic® 6.0Visual Basic for Applications (VBA) 和脚本语言访问目录信息的最常用方法。因此,许多关于 Active Directory 的示例和新闻组发言都集中在了 ADSI 上。

令人高兴的是,ADSI 中使用的许多概念都可以经过轻松转换用在 System.DirectoryServices 中,例如 ADSI 示例中显示的 ADSI 路径或属性名。此外,还可以在 COM .NET 库之间使用混合代码。System.DirectoryServices 命名空间中有若干方法和属性可以将 ADSI 对象作为参数接收。当然,即使您从未使用过 ADSISystem.DirectoryServices 也是很容易学习和使用的。

建立连接

System.DirectoryServices 命名空间中有两个主要类,即 DirectoryEntry DirectorySearcher。(还有其他几个类,但这两个是首先需要使用的类。)搜索将使用 DirectorySearcher 类(名称与其功能很般配)来执行。使用该类时,可以不设置任何选项,也可以提供一个根 DirectoryEntry 对象。

如果创建了 DirectoryEntry 对象并用作搜索的根,则需要指定路径以说明要连接的服务,还要指定您的安全凭据。创建此单一对象时即建立了指向您的目录的连接。通过提供此 DirectoryEntry 对象作为搜索的根,您便可以使用此连接进行查询。或者,如果不指定根对象,DirectorySearcher 将自动绑定到当前域,并使用您的 Microsoft® Windows® 凭据进行身份验证。

绑定到目录路径

要连接到 Active Directory,您可以使用全局编录 (GC://) 语法来指定路径,也可以使用标准的 LDAP 路径 (LDAP://)。具体使用哪种语法和路径视您的网络环境而定。例如,在我自己的内部网络中,我会使用全局编录语法,因为这使我可以在整个企业网络范围(整个 Active Directory 森林)内进行搜索。我没有使用包含服务器名称的路径,而是仅指定了“GC://dc=home,dc=duncanmackenzie,dc=net”(假设我的域称为 home.duncanmackenzie.net),该路径会告知 ADSI 连接到全局编录服务器的指定域。当然如果您愿意,也可以不指定任何路径来建立连接。如果使用 DirectorySearcher 对象而未提供任何根对象,它将自动使用当前域进行搜索。有关为您的网络确定正确路径的详细信息,

验证目录

指定 LDAP GC 路径后,接下来要处理的是安全凭据。您可以使用 DirectoryEntry 类的构造函数来指定用户 ID 和密码,或者在创建该对象的实例后再设置用户 ID 和密码。请不要在您的代码中存储实际的密码/用户 ID,而应当从用户那里检索此信息,或者最好使用集成身份验证。

Dim rootEntry _

As New DirectoryEntry("GC://dc=home,dc=duncanmackenzie,dc=net", _

userID.Text, _

password.Text)

注意:要使上述代码能够工作,您需要在项目中引用 System.DirectoryServices,并在源文件的顶端添加 Imports System.DirectoryServices (Visual Basic .NET) using System.DirectoryServices; (C#) 代码行。

如果您不指定用户 ID 和密码,System.DirectoryServices 将尝试使用 Windows 集成身份验证进行连接。通常,我倾向于选择集成身份验证,至少在 Microsoft® Windows 窗体应用程序中会使用这种验证,因为这样每个用户可以仅使用各自已有的 Active Directory 权限集。使用硬编码的用户 ID 和密码可能会使我的应用程序赋予用户大于所预期的 Active Directory 访问权限,从而带来安全隐患。

执行搜索

创建初始(根)DirectoryEntry 后,您就可以使用 DirectorySearcher 类来执行查询。执行简单的搜索只需要以下几个步骤:

  • 创建 DirectorySearcher 的实例,可以选择使用根 DirectoryEntry 对象。
  • 将搜索筛选器设置为描述搜索条件的字符串。
  • 使用要为每个搜索结果检索的 Active Directory 属性列表填充 PropertiesToLoad 集合。
  • 执行 FindAll 方法并检索结果。

如果需要,您可以跳过几乎所有这些步骤,使用默认设置执行搜索。如果不提供根 DirectoryEntry 对象,DirectorySearcher 将绑定到当前域;如果不提供搜索筛选器,将默认检索所有对象;如果不指定任何 PropertiesToLoad 值,将检索所有属性(您可以读取的属性)。这些默认设置使得 DirectoryServices 类的使用非常简便。不过,我建议您至少使用一个筛选器来限制搜索,并限制加载的属性集,除非您的应用程序确实需要所有属性和所有对象。

Dim searcher As New DirectorySearcher(rootEntry)

searcher.PropertiesToLoad.Add("cn")

searcher.PropertiesToLoad.Add("mail")

' searcher.PropertiesToLoad.AddRange(New String() {"cn", "mail"})

' 将同样有效并节省一些代码

searcher.Filter = "(&(anr=duncan)(objectCategory=person))"

Dim results As SearchResultCollection

results = searcher.FindAll()

附加选项

还有许多附加搜索选项。这些选项不是必需的,但可以显著影响查询的效果。以下是其中一些附加选项及其简要使用说明:

  • CacheResults 确定搜索结果是否存储在本地客户端计算机上。使用此设置,可以向前或向后浏览搜索结果,否则只能以向前模式浏览结果。
  • ClientTimeoutServerTimeLimit ServerPageTimeLimit 都是用于防止搜索运行时间过长的超时值。第一个值 ClientTimeout 用于控制客户端的等待时间。其他两个时间限制由服务器施加。我建议至少设置 ClientTimeout,以避免您的应用程序出现无限等待的情况。但是,有一点需要注意,即如果到达了某个基于服务器的时间限制,则返回到达该时间点时检索到的条目;如果首先到达了客户端时间限制,则不会返回任何内容。请记住,服务器本身具有可由管理员配置的时间限制,因此在到达您指定的时间限制之前可能会出现超时,从而返回不完整的结果。
  • PageSize 确定一次返回的项数。如果不设置 PageSize 属性或将其设置为 0,则一次返回所有结果。不过,使用分页会使您的应用程序看起来更方便一些。请记住,服务器会确定搜索中返回对象的最大数目,以避免用户造成系统负载过重。所以建议您始终使用分页功能,特别是预计会出现很大的结果集时。在本文末尾的演示中,我将向您说明如何与多线程一起使用分页功能,以生成搜索窗体,并且使用户不必等待。

使用结果

执行搜索后,您将得到一个返回的 SearchResultCollection 类的实例,该实例使您可以对结果进行枚举。这时是否使用了分页并不重要,您仍然是以相同的方式在访问搜索结果。如果下一页的结果尚不可用,您的枚举将被锁定,直至结果就绪。

Dim result As SearchResult

For Each result In results

MessageBox.Show(result.Properties("cn")(0))

Next

创建快速搜索演示

经过上述所有步骤,我创建了一个针对 Active Directory 执行搜索的简单应用程序。该示例可以通过本文下载(请单击本文顶部的链接)。如果您希望自己构建,也可以遵照本文进行操作。

为正确配置搜索(包括安全性选项),我必须在所开始的空白 Windows 窗体中设置若干控件,其中包括四个 TextBox 控件(根路径、搜索字符串、用户 ID 和密码)、一个 CheckBox 控件(使我可以在集成身份验证和使用用户 ID 及密码之间进行切换)、一个按钮(用于执行搜索)和一个 ListBox 控件(用于显示结果)。我花了一些时间来安排这些控件,并设置了定位属性以使窗体看起来美观一些,使界面可以调整大小,当然这一步完全是可选的。

1:使用最终完成的窗体可以执行简单的搜索

现在,我的按钮的单击事件将接收在四个 TextBox CheckBox 中输入的值,并执行搜索。

Dim rootEntry _

As New DirectoryEntry(rootPath.Text)

If Not integratedAuth.Checked Then

rootEntry.Username = userID.Text

rootEntry.Password = password.Text

End If

Dim searcher As New DirectorySearcher(rootEntry)

searcher.PropertiesToLoad.Add("cn")

searcher.PropertiesToLoad.Add("telephoneNumber")

' searcher.PropertiesToLoad.AddRange(New String() {"cn", "mail"})

' 将同样有效并节省一些代码

searcher.PageSize = 5

searcher.ServerTimeLimit = New TimeSpan(0, 0, 30)

searcher.ClientTimeout = New TimeSpan(0, 10, 0)

searcher.Filter = searchString.Text

Dim queryResults As SearchResultCollection

queryResults = searcher.FindAll()

得到结果后,将它们逐一添加到 ListBox 中。

Dim result As SearchResult

For Each result In queryResults

results.Items.Add(result.Properties("cn")(0))

Next

注意:知道要使用哪些属性以及如何检索这些属性是使用 System.DirectoryServices 时遇到的复杂概念之一。这里有一个很好的 Active Directory 属性名参考资源,至少可供您在处理用户和帐户时使用,即 SDK 参考(英文)。它将属性名映射到 Windows 中用户和组管理单元的信息中。还有一点需要注意,即许多属性可以有多个值,因此属性值可以显示为包含任意多个值的数组。在该示例中,我通过访问属性值数组的第一个元素来检索 cn 属性的值。之所以能够这样做,是因为我碰巧知道 cn 仅包含一个值。当使用多种不同的属性时,您可能需要检查每个属性以确定赋予它的是单个值还是多个值。MSDN 上的 Active Directory Schema 参考为您介绍了这方面内容,它提供了每个属性的详细信息,包括它们的数据类型以及包含的是单个值还是多个值。

完成上述示例后,我们可以从中了解如何针对 Active Directory 执行搜索,但还有两个问题需要解决。第一个问题是,该搜索与我的用户界面(窗体)在同一线程中执行。也就是说,当我的代码等待 DirectoryServices 对象的响应时,我的用户界面将无法响应。第二个问题是,我需要手动向 ListBox 中添加检索结果,而许多编程人员更习惯于使用数据绑定将数据集绑定到用户界面。在本文后面的内容中,我将介绍如何使用后台线程提供具有灵活响应的用户界面,以及如何在运行时创建 DataTable 以便提供数据绑定功能。

将搜索转至后台线程

每当我打算在后台线程中运行任务(所有任务,不只是 DirectoryServices 操作)时,我始终会采用同一个模式。我会先介绍一下这个模式,然后向您展示如何将其应用到目录搜索的特定任务中。

我创建一个类来封装后台任务。这个类包括:

  • 用于配置该任务的属性。
  • 一个不能接收任何自身参数(这也是我使用属性的原因)的方法,该方法将在新线程中执行。
  • 一个或多个触发后用于传递后台任务的进度或结束信息的事件。

注意:如果我在 Windows 窗体中使用此模式,则需要避免因后台任务生成的事件而导致更新窗体本身,并且需要使用窗体的 Invoke 方法在两个线程间正确封送。

将此模式应用于前面创建的搜索示例(在下载代码中创建“DS Background”项目)时,需要添加一个新类 BackgroundSearchBackgroundSearch 具有若干属性,因此可以适当配置搜索,同时它还有一个 StartSearch 方法。每找到一个结果时,便会生成一个 ResultFound(这一名称同样一目了然)事件。完成整个搜索后,将触发 SearchCompleted 事件。

Imports System

Imports System.DirectoryServices

Public Class BackgroundSearch

Dim m_FilterString As String

Dim m_PageSize As Integer

Dim m_RootPath As String

Dim m_PropertiesToLoad() As String

Dim m_IntegratedAuthentication As Boolean

Dim m_UserID As String

Dim m_Password As String

Public Property IntegratedAuthentication() As Boolean

Get

Return m_IntegratedAuthentication

End Get

Set(ByVal Value As Boolean)

m_IntegratedAuthentication = Value

End Set

End Property

Public Property UserID() As String

Get

Return m_UserID

End Get

Set(ByVal Value As String)

m_UserID = Value

End Set

End Property

Public Property Password() As String

Get

Return m_Password

End Get

Set(ByVal Value As String)

m_Password = Value

End Set

End Property

Public Property PropertiesToLoad() As String()

Get

Return m_PropertiesToLoad

End Get

Set(ByVal Value As String())

m_PropertiesToLoad = Value

End Set

End Property

Public Property FilterString() As String

Get

Return m_FilterString

End Get

Set(ByVal Value As String)

m_FilterString = Value

End Set

End Property

Public Property PageSize() As Integer

Get

Return m_PageSize

End Get

Set(ByVal Value As Integer)

m_PageSize = Value

End Set

End Property

Public Property RootPath() As String

Get

Return m_RootPath

End Get

Set(

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/7186556/viewspace-918146/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/7186556/viewspace-918146/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值