分享
 
 
 

AD & ADSI入门

王朝vb·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

AD简介

Active Directory(以下简称AD)可以认为是一个大的层次结构数据库,集中存储的内容必须遵循AD当前所定义的Schema。我觉得AD中最重要的内容就是Schema,然后是ADSI。

Schema定义了数据存储的格式。包括类(classSchema),分为抽象类(Abstract)、附属类(Auxiliary)

和结构类(Structure)三种;属性(attributeSchema),分为单值和多值属性;以及类和属性之间的关系,分为可选属性和必要属性。AD中的Schema相当于全局的Catalog,在整个AD的forest中是全局唯一的,任何的修改都会被同步。所以有关Schema的修改需要有Schema Administrators的权限。而且Schema的内容只能增加,不能删除,过程是不可逆的,最多只能禁止一些属性或者类,而且还有诸多限制,详见文档。

在AD中存储的数据被当作一个一个的对象,每一个对象是一个classSchema的实例。都有一个唯一的路径访问。AD中对象的路径由所支持的Provider决定,Windows 2000默认的Provider有四个,安装IIS的话会增加一个IIS的Provider,对IIS进行管理。通常使用比较多的是LDAP,可以通过这个Provider针对域用户以及其他扩展信息进行访问和管理。WinNT是针对NT4的帐号管理,估计是向下兼容。另外两个是对Netware的DN的访问,对我们而言不太会用到,不赘述了。

ADSI(Active Directory Service Interface)是用来对AD中存储数据的访问接口,我认为他是一个架空的框架,与具体的数据访问无关,只是给上层应用提供一个统一的接口。实际工作的就是Provider,应当是Provider访问数据,然后包装成ADSI要求的形式,这些工作对用用户来说完全透明。

另一个比较爽的地方是ADSI提供了比较好的扩展的方式,你能够非常容易的增加新的类,或者给已有的类增添新的方法。

需要解释一下,虽然ADSI和AD经常一起出现,但是AD和ADSI是两码事,ADSI不仅仅能访问AD,还可以访问IIS以及Netware所存储的数据。你只要按照要求提供相应的Provider,ADSI可以干任何事情。

AD编程

AD的编程到目前来看涵盖的内容非常多,从最粗糙的使用ADSI对AD中已有的数据访问,到比较高级的扩展AD。我认为扩展AD才是这部分编程中比较重要的内容,因为针对任何具体的应用多绘有自己特定的信息,而应用AD主要是为了利用MS提供的安全性以及分布式存储,如果将这两方面结合起来,就需要为了自身的应用对AD进行扩展。

如果需要为已有的类(接口类)添加方法,那么需要编写AdsExtension类;如果需要在AD中存储扩展信息,就需要修改Schema,增加新的类(classSchema)或者属性(attributeSchema);更进一步的话,完全可以自己实现一个Provider,实现自己的查询和数据存储的方式,这一部分内容已经不仅仅局限于AD了。

通过ADSI访问AD

通过ADSI访问AD比较简单,实际上是应用WinNT和LDAP这两个Provider。除了通用接口IAds、IAdsContainer、IAdsDirectorySearch等以外,Windows默认提供了一些接口类如IAdsUser、IAdsGroup,当安装了一些基于AD的服务之后,又会增加一些专有接口,如安装Exchange 2000之后,会出现Person,同时扩展了User。

在使用AD的过程中比较重要的一个问题是访问者的权限,如果使用GetObject的方式操作,那么应用程序是以当前登录用户的权限访问AD,很多的写操作是被拒绝的。使用IAdsOpenDSObject->OpenDSObject可以指定操作对象的用户,当然这就需要实现得到指定用户的口令。

第二个需要注意的地方就是AD的Path,有两个最常用的前缀(姑且这么叫吧):CN(Common Name)和DC(Domain Controller)。另外对于LDAP从左到右范围增大,而WinNT从左到右范围是减小,比如访问我的帐号,路径分别为LDAP://CN=mittermeyer,CN=Users,DC=cn,DC=corp,DC=company,DC=com,WinNT://cn.corp.company.com/Users/mittermeyer。另外据说AD是区别大小写的,我看下来他有一种数据类型是区别大小写的字符串,但是路径这里好像无所谓,CN=和cn=都行。

第三个需要注意的地方就是查询语法。查询的话,一共提供了两种方式,一个是IAdsSearchDirectory接口,IAdsDirectorySearch完成查询过程和处理查询结果的全部工作,我个人认为这种方式不太适合VB的程序;另一种方式是使用ADO。AD针对ADO有一个Provider(ADsDSOObject),使用这种方式返回一个ADO.Recordset,处理结果和关系型数据库的查询完全一致,这种方式VB比较容易上手。ADO的方式查询可以使用SQL的语法,也可以使用LDAP的语法;而IAdsDirectorySearch只能使用到的是LDAP的语法。

这一部分比较有趣的是扩展接口,就是写一个接口作为已经存在的接口类的扩展。扩展接口本身只是继承IDispatch就可以了,但是如果需要支持后期绑定,那么还需要实现IAdsExtension所要求的一系列方法,看上去是模板,就是一个套路,所以这部分工作还是比较简单的。

关键是把自己编写的接口和已经存在的接口类关联,嘿嘿!也很简单,只要在注册表里加一项就可以了。(MS想到的方法总是比较容易理解,不过在整体框架那里还是花了很多心思的,所以架子有了扩展就容易了。)例如:以及下就是Exchange加的对User地扩展,它表明Exchange针对User有一个扩展的CoClass--Mailbox,其中包含了两个接口IMailRecipient和IMailboxStore。

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ADs\Providers\LDAP\Extensions\User]

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ADs\Providers\LDAP\Extensions\User\{25150F21-5734-11D2-A593-00C04F990D8A}]

"Interfaces"=hex(7):7b,00,32,00,35,00,31,00,35,00,30,00,46,00,34,00,31,00,2d, 00,35,00,37,00,33,00,34,00,2d,00,31,00,31,00,44,00,32,00,2d,00,41,00,35,00, 39,00,33,00,2d,00,30,00,30,00,43,00,30,00,34,00,46,00,39,00,39,00,30,00,44, 00,38,00,41,00,7d,00,00,00,7b,00,32,00,35,00,31,00,35,00,30,00,46,00,34,00, 30,00,2d,00,35,00,37,00,33,00,34,00,2d,00,31,00,31,00,44,00,32,00,2d,00,41, 00,35,00,39,00,33,00,2d,00,30,00,30,00,43,00,30,00,34,00,46,00,39,00,39,00, 30,00,44,00,38,00,41,00,7d,00,00,00,00,00

扩展Schema

首先,我们知道attributeSchema和classSchema都存储在同一个层次下,我们可以通过这样的路径访问:LDAP://computername/Schema 之下(另外一种访问路径是CN=Schema,CN=Configuration,<DC=forestroot>),所以在这个Container下我们可以枚举到整个AD中所有的attributeSchema和classSchema。但是我发觉有一个有趣的现象,如果不是以域用户的身份去访问,那么不能得到全集,获得的是Abstract Class和相关的属性,无法得到Structure Class,例如:User。

其次,针对attributeSchema和classSchema都有一些特性:

OID(governsID),LDAP所需要的对象的唯一表示符,这是一个字符串,但是不同于GUID根据本机信息生成,而是逐级分配的属性结构,最上层由ISO分配,逐级授权,所以很麻烦。MS提供了一个工具OIDGEN.exe,随Windows 2000的Resource Kit发布,我不知道即使是用这样的工具生成的新ID能否运行在实际的扩展系统中,还是必须通过MS的认证。

schemaIDGUID,用于访问控制目录中控制访问这个类的对象。通过这个ID而不是名称来访问类的对象实例。GUID还是非常好处理的,可以通过Windows自身的API获得。

其他的就是各类名称(cn,LDAPDisplayName,adminDisplayName),在不同的工具或者场合显示区别类或者属性,这些名字只要保证全局唯一即可。此外classSchema和attributeSchema各有一些特定的必备属性。

扩展Schema包括以下几部分的工作:新增/禁止attributeSchema,新增/禁止classSchema,修改Property与classSchema的关系。

新增attributeSchema和classSchema,通过IAdsContainer.Create,在Schema存储的路径下新建子节点,然后给必要的属性赋值,最后提交即可。

禁止attributeSchema和classSchema,可以通过“废弃”的方式禁止一个现存的类或者属性。即获得这个classSchema或者attributeSchema,将他的isDefunct属性置为True即可;反之只要将isDefunct属性置为False即可恢复。当然这个操作也存在一系列的限制,例如:禁止一个属性,那么将阻止创建所有所有必须包含该属性的类的实例。

修改Property与classSchema的关系,因为决定每一个classSchema中包含哪些attributeSchema,其实是指定classSchema的“mustContain”和“mayContain”,这两个多值属性(字符串数组)分别表示表示所包含的必要属性和可选属性。反过来,可以通过IAdsClass.MandantoryProperties和IAdsClass.OptionalProperties读取。

实现Provider

暂时没有研究。

实例

枚举对象。下面这个例子枚举了所有通过RC的接口添加到AD中的用户组和帐号。此例中IAdsContainer.Filter为一个需要筛选的类名数组,如果为空,则表示返回所有类型的对象。

Public Function EnumGroups() As VBA.Collection

Dim adDomain As IADsContainer

Dim adGroup As IADsGroup

Dim nResult As VBA.Collection

If m_sAdmin <> vbNullString Then

Set adDomain = m_adRoot.OpenDSObject("LDAP://" & m_sExchServer & "/CN=Users," & m_sDomain, _

m_sAdmin, m_sAdminPwd, ADS_SECURE_AUTHENTICATION)

Else

Set adDomain = GetObject("LDAP://CN=Users," & m_sDomain)

End If

If adDomain Is Nothing Then Exit Function

Set nResult = New VBA.Collection

adDomain.Filter = Array("group","user"”)

On Error Resume Next

Dim sName As String

Dim sType As String

For Each adGroup In adDomain

sName = Right(adGroup.Name, Len(adGroup.Name) - 3) ' filter "CN="

Debug.Print sName

sType = adGroup.Get(cPropCustomType)

If Err.Number = 0 And sType = cTypeRC Then

nResult.Add sName, sName

End If

Err.Clear

Next

Set EnumGroups = nResult

End Function

添加一个用户以及用户相关的邮箱,这是一个相对复杂的利用ADSI的示例,其他类似的操作就不赘述了。这里用到的就是ADSI和Exchange针对ADSI中IAdsUser对象的扩展。斜体的那一段代码颇值得回味,在VB中非常简单的一句话,背后有一套复杂的逻辑。

添加用户组和组邮箱的操作类似,不同的是组邮箱不是一个物理邮箱,而是一个邮箱列表,通过IMailRecipient.MailboxEnabled使之有效即可。

' add new user to Domain and create mailbox for it

Public Function AddAccountEx(ByVal sAccount As String, ByVal sFullName As String, ByVal sDesc As String, _

ByVal sPassword As String) As Long

Dim adDomain As IADsContainer

Dim adNewUser As IADsUser

Dim oMailStore As CDOEXM.IMailboxStore

Dim oExchServer As CExchageManager

If m_sAdmin <> vbNullString Then

Set adDomain = m_adRoot.OpenDSObject("LDAP://CN=Users," & m_sDomain, _

m_sAdmin, m_sAdminPwd, ADS_SECURE_AUTHENTICATION)

Else

Set adDomain = GetObject("LDAP://CN=Users," & m_sDomain)

End If

' create a account

Set adNewUser = adDomain.Create("user", "cn=" & sAccount)

adNewUser.Put "sAMAccountName", sAccount

adNewUser.Put "userPrincipalName", sAccount & "@" & Domain

adNewUser.FullName = sFullName

adNewUser.Description = sDesc

adNewUser.SetInfo

adNewUser.SetPassword sPassword

adNewUser.AccountDisabled = False

' create mailbox for this account

Set oExchServer = New CExchageManager

oExchServer.Connect m_sExchServer ' Get Exchange Server's Information

Set oMailStore = adNewUser

Call oMailStore.CreateMailbox("LDAP://" & m_sExchServer & "/" & oExchServer.DefaultMailboxStore)

adNewUser.SetInfo

' enable immediate-logon for the user

adNewUser.Put "msExchUserAccountControl", 2

adNewUser.SetInfo

End Function

查找。通过ADO查询比较简单,只是属性的类型,特别是一些多值属性需要额外注意。

这个例子是查询所有指定域中所有的组,其中description就是一个多值属性。

Public Function SearchGroup() As ADODB.Recordset

Dim oResult As ADODB.Recordset

Dim oCommand As ADODB.Command

Dim sConnectionStr As String

If m_sAdmin = vbNullString Then

sConnectionStr = "Provider=ADsDSOObject"

Else

sConnectionStr = "Provider=ADsDSOObject;UID=" & m_sAdmin & ";PWD=" & m_sAdminPwd

End If

Set oCommand = New ADODB.Command

With oCommand

.ActiveConnection = sConnectionStr

.CommandTimeout = 15

.CommandText = "SELECT name,description FROM 'LDAP://" & m_sDomain _

& "' WHERE objectCategory='group'"

Debug.Print .CommandText

.Properties("searchscope") = ADS_SCOPE_SUBTREE

.Properties("Chase referrals") = ADS_CHASE_REFERRALS_EXTERNAL

Set oResult = .Execute

End With

If Not oResult Is Nothing Then

Do Until oResult.EOF

Debug.Print oResult("name"), oResult("description")(0)

oResult.MoveNext

Loop

End If

End Function

PS:很久以前写的东西,望指正。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有