您好,NW。您可能知道,在美国 Internet 赌博是非法的。真遗憾,因为如果不非法我们愿意赌 10 美元,这就是您问题的答案:搜索 Active Directory。
而且,我们会很不情愿地说“我们告诉过您,”好,您猜会怎样:
On Error Resume Next
Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection
objCommand.Properties("Page Size") = 1000
objCommand.CommandText = _
"<LDAP://dc=fabrikam,dc=com>;" & _
"(&(objectCategory=User)(userAccountControl:1.2.840.113556.1.4.803:=65536));" & _
"Name;Subtree"
Set objRecordSet = objCommand.Execute
objRecordSet.MoveFirst
Do Until objRecordSet.EOF
Wscript.Echo objRecordSet.Fields("Name").Value
objRecordSet.MoveNext
Loop
您说对了:本来我们应该不止赌 10 美元。我们刚才在想什么呢?
实际上,我们没有就此打赌并非坏事。事实上,我们耍了花招:我们得到了一些内部信息。毕竟,每次寻找与 Active Directory 相关的东西时,答案总是如出一辙:搜索 Active Directory。很久以前我们就知道这个答案了,甚至在您提问之前就知道了。
我们只是不知道(至少在本例中)如何进行实际搜索。我们不想过多谈论编写 Active Directory 搜索脚本的细节;相关背景信息可在脚本故事两篇系列文章 Dude,我的打印机在哪儿? 中找到。但我们要花一两分钟来谈谈我们最终用来执行搜索的查询,此查询可能与您习惯使用的 Active Directory 查询略有不同:
objCommand.CommandText = _
"<LDAP://dc=fabrikam,dc=com>;" & _
"(&(objectCategory=User)(userAccountControl:1.2.840.113556.1.4.803:=65536));" & _
"Name;Subtree"
如果您在想,“咦,这个查询看起来不像 SQL 查询,”嗯,这是一个充分的理由,可以证明:此查询不是 SQL 查询。而是一个 LDAP 查询语法,此语法可与 SQL 查询检索相同的信息,即使查询方式较为隐秘。
但是,如果的确如此,我们为什么要使用这一看似奇怪的语法而不用更熟悉(更方便)的 SQL 查询语法?究其原因是因为确定密码是否过期的属性 (ADS_UF_DONT_EXPIRE_PASSWD) 不是“独立”属性;换言之,您不可能使用类似以下的代码获得它的值:
Wscript.Echo objUser.ADS_UF_DONT_EXPIRE_PASSWD
而实际上,此值是 userAccountControl 属性中的众多“标志”之一。userAccountControl 属性是一种 bitmask 属性,一个属性可容纳多个属性值;这一属性还可告诉您密码是否会过期、是否已过期、帐户是否被禁用等。在本例中,如果值为 65536 的标志切换至“开”,则密码永不过期;如果此标志切换为关,则密码会过期。
|
注意。是的,我们知道:许多人不明白我们刚才在说什么。要了解对 bitmask 的简介,您可以看看 Microsoft Windows 2000 脚本指南 中的这部分内容。 |
那么,这对我们来说有什么不同呢?嗯,有很大差别:您无法(至少无法轻易地)使用 SQL 查询搜索 bitmask 属性中的单个值。但使用 LDAP 查询语法可以做到这一点,这正是我们所做的。这就如同由几个脚本专家驾驶的车,虽然不太好看,但却能正常行驶。(对不起,专家们,但忠言逆耳。)
至于查询,我们将简单介绍各部分的功能,以便您了解整个查询是如何进行的。例如:
|
项目 |
说明 |
|
<LDAP://dc=fabrikam,dc=com> |
搜索的起点。我们要搜索所有 Active Directory,因此这表示从根目录开始(即,fabrikam.com)。 |
|
& |
相当于 SQL 查询中的 AND 运算符。需要此符号是因为我们要搜索用户而且要搜索 userAccountControl 属性中的特定值。必须同时满足这两个条件,才能返回对象。 |
|
(objectCategory=User) |
限制返回至用户帐户的数据。 |
|
(userAccountControl:1.2.840.113556.1.4.803:=65536) |
表示我们只想返回 65536 的 userAccounControl 标志切换为开的帐户,即密码未过期的用户帐户。以下,我们将略微详细地介绍这一看似奇怪的代码块。 |
|
Name |
我们要返回的 Active Directory 属性。我们只要求返回一个属性:Name。要返回其他属性,将它们添加到 Name 后即可,相互间用逗号分隔: Name,cn,AdsPath
|
|
Subtree |
表示搜索的类型。指定 Subtree 可使脚本搜索 fabrikam.com 根目录下的所有 OU 和容器。由于必须在根目录下找到所有的 OU 和容器,脚本就会搜索所有 Active Directory。 |
尽管可能看似奇怪,但其中大部分都有实际作用:您了解各部分代表的含意后,就不难解读(甚至修改) LDAP 查询了。对了,还有一个例外:
(userAccountControl:1.2.840.113556.1.4.803:=65536)
是的,我们知道这看起来有多奇怪,我们也希望一些奇怪的事从我们的视线中消失。但是,如果略过表象,这的确相当简单。1.2.840.113556.1.4.803 不过是一个 LDAP 匹配对象规则标识符 (OID)。所以,这不过是以一种有些笨拙的方式表示:“显示值为 65536 的 userAccountControl 标志为开的所有对象。”它的确仅此而已。这也意味着只需将 65536 替换为需要的值即可方便地搜索 userAccountControl 中的其他属性值。需要所有禁用帐户的列表吗?下面就是:
(userAccountControl:1.2.840.113556.1.4.803:=2)
很酷吧。
是的,我们知道您的下一个问题:您能将我们原来的查询改为找到没有不过期密码的所有用户吗?当然,尽管此语法看起来又有点奇怪。不再全部介绍了,以下就是修改后的代码,可搜索密码会过期的用户帐户:
(!(userAccountControl:1.2.840.113556.1.4.803:=65536))
在本例中,! 表示“不等于”。赌 10 美元,您肯定不知道这一点,是吗?
嗯。好了,让我们知道您把支票寄到哪儿就行了。这对我们的支出帐户来说可能不妙。(如果我们中有人有支出帐户的话,至少不会有利于此帐户。)