AD & ADSI入門 (轉)
AD & ADSI入門 (轉)[@more@]
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中物件的路徑由所支援的Provr決定, 2000預設的Provider有四個,IIS的話會增加一個IIS的Provider,對IIS進行管理。通常使用比較多的是,可以透過這個Provider針對域以及其他擴充套件資訊進行訪問和管理。WinNT是針對NT4的帳號管理,估計是向下相容。另外兩個是ware的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、IAdntainer、IAdsDirectorySearch等以外,Windows預設提供了一些介面類如IAdsUser、IAdsGroup,當安裝了一些基於AD的服務之後,又會增加一些專有介面,如安裝 2000之後,會出現Person,同時擴充套件了User。
在使用AD的過程中比較重要的一個問題是訪問者的許可權,如果使用Get的方式操作,那麼應用是以當前登入使用者的許可權訪問AD,很多的寫操作是被拒絕的。使用IAdsOpenDSObject->OpenDSObject可以指定操作物件的使用者,當然這就需要實現得到指定使用者的口令。
第二個需要注意的地方就是AD的Path,有兩個最常用的字首(姑且這麼叫吧):CN(Common Name)和DC(ain Controller)。另外對於LDAP從左到右範圍增大,而WinNT從左到右範圍是減小,比如訪問我的帳號,路徑分別為,WinNT://cn.corp.company.com/Users/mittermeyer。另外據說AD是區別大小寫的,我看下來他有一種資料型別是區別大小寫的字串,但是路徑這裡好像無所謂,CN=和cn=都行。
第三個需要注意的地方就是查詢語法。查詢的話,一共提供了兩種方式,一個是IAdsSearchDirectory介面,IAdsDirectorySearch完成查詢過程和處理查詢結果的全部工作,我個人認為這種方式不太適合VB的程式;另一種方式是使用ADO。AD針對ADO有一個Provider(ADsDSOObject),使用這種方式返回一個ADO.Recordset,處理結果和關係型資料庫的查詢完全一致,這種方式VB比較容易上手。ADO的方式查詢可以使用的語法,也可以使用LDAP的語法;而IAdsDirectorySearch只能使用到的是LDAP的語法。
這一部分比較有趣的是擴充套件介面,就是寫一個介面作為已經存在的介面類的擴充套件。擴充套件介面本身只是繼承IDispatch就可以了,但是如果需要支援後期繫結,那麼還需要實現IAdsExtension所要求的一系列方法,看上去是模板,就是一個套路,所以這部分工作還是比較簡單的。
關鍵是把自己編寫的介面和已經存在的介面類關聯,嘿嘿!也很簡單,只要在登錄檔里加一項就可以了。(MS想到的方法總是比較容易理解,不過在整體框架那裡還是花了很多心思的,所以架子有了擴充套件就容易了。)例如:以及下就是Exchange加的對User地擴充套件,它表明Exchange針對User有一個擴充套件的CoClass--box,其中包含了兩個介面IMailRecipient和IMailboxStore。
關鍵是把自己編寫的介面和已經存在的介面類關聯,嘿嘿!也很簡單,只要在登錄檔里加一項就可以了。(MS想到的方法總是比較容易理解,不過在整體框架那裡還是花了很多心思的,所以架子有了擴充套件就容易了。)例如:以及下就是Exchange加的對User地擴充套件,它表明Exchange針對User有一個擴充套件的CoClass--box,其中包含了兩個介面IMailRecipient和IMailboxStore。
[HKEY_LOCAL_MACHINESOFTWAREADsProvidersLDAPExtensionsUser]
[HKEY_LOCAL_MACHINESOFTWAREMicrosoftADsProvidersLDAPExtensionsUser{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
"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都儲存在同一個層次下,我們可以透過這樣的路徑訪問: 之下(另外一種訪問路徑是CN=Schema,CN=Configuration,>),所以在這個Container下我們可以列舉到整個AD中所有的attributeSchema和classSchema。但是我發覺有一個有趣的現象,如果不是以域使用者的身份去訪問,那麼不能得到全集,獲得的是Abstract Class和相關的屬性,無法得到Structure Class,例如:User。
其次,針對attributeSchema和classSchema都有一些特性:
OID(governsID),LDAP所需要的物件的唯一表示符,這是一個字串,但是不同於GUID根據本機資訊生成,而是逐級分配的屬性結構,最上層由ISO分配,逐級授權,所以很麻煩。MS提供了一個工具OIDGEN.exe,隨的Re Kit釋出,我不知道即使是用這樣的工具生成的新ID能否執行在實際的擴充套件中,還是必須透過MS的。
schemaIDGUID,用於訪問控制目錄中控制訪問這個類的物件。透過這個ID而不是名稱來訪問類的物件例項。GUID還是非常好處理的,可以透過Windows自身的獲得。
其他的就是各類名稱(cn,LDAPDisplayName,adminDisplayName),在不同的工具或者場合顯示區別類或者屬性,這些名字只要保證全域性唯一即可。此外classSchema和attributeSchema各有一些特定的必備屬性。
OID(governsID),LDAP所需要的物件的唯一表示符,這是一個字串,但是不同於GUID根據本機資訊生成,而是逐級分配的屬性結構,最上層由ISO分配,逐級授權,所以很麻煩。MS提供了一個工具OIDGEN.exe,隨的Re Kit釋出,我不知道即使是用這樣的工具生成的新ID能否執行在實際的擴充套件中,還是必須透過MS的。
schemaIDGUID,用於訪問控制目錄中控制訪問這個類的物件。透過這個ID而不是名稱來訪問類的物件例項。GUID還是非常好處理的,可以透過Windows自身的獲得。
其他的就是各類名稱(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讀取。
新增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="
De.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
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="
De.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 ullName As String, ByVal sDesc As String, _
ByVal sPass 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
Public Function AddAccountEx(ByVal sAccount As String, ByVal ullName As String, ByVal sDesc As String, _
ByVal sPass 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
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.Creatbox("LDAP://" & m_sExchServer & "/" & oExchServer.DefaultMailboxStore)
adNewUser.SetInfo
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.Creatbox("LDAP://" & m_sExchServer & "/" & oExchServer.DefaultMailboxStore)
adNewUser.SetInfo
' enable immediate-logon for the user
adNewUser.Put "msExchUserAccountControl", 2
adNewUser.SetInfo
End Function
adNewUser.Put "msExchUserAccountControl", 2
adNewUser.SetInfo
End Function
- 查詢。透過ADO查詢比較簡單,只是屬性的型別,特別是一些多值屬性需要額外注意。
這個例子是查詢所有指定域中所有的組,其中description就是一個多值屬性。
Public Function SearchGroup() As ADO.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 = " 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
L
End If
End Function
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 = " 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
L
End If
End Function
PS:很久以前寫的東西,望指正。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-962353/,如需轉載,請註明出處,否則將追究法律責任。
請登入後發表評論
登入
全部評論
相關文章
- C++Builder使用ADSI建立web站點 (轉)C++UIWeb
- wsad5入門 (轉)
- hadoop 入門Hadoop
- pthread 入門thread
- ADO資料庫程式設計入門(轉)資料庫程式設計
- Hadoop快速入門Hadoop
- Laradock 入門配置
- Vivado入門教程
- linux新手入門――shell入門(轉)Linux
- 【轉】Zookeeper入門
- Emacs入門(轉)Mac
- iptables 入門(轉)
- CSS入門(轉)CSS
- Linux程式設計入門 fork/pthread/signals(轉)Linux程式設計thread
- appuploader 入門使用APP
- python tornado 入門Python
- Apache Hadoop 入門教程ApacheHadoop
- Pytorch入門-dataloaderPyTorch
- 轉載:mybatis入門MyBatis
- COM入門(轉載)
- GRUB入門教程(轉)
- CSS快速入門(轉)CSS
- BSD socket入門(轉)
- rpm 入門(轉)
- Azure AD(一)入門認識
- webpack 入門之 loader 案例Web
- Hadoop 專欄 - MapReduce 入門Hadoop
- Hadoop的HDFS 入門使用Hadoop
- 【安卓筆記】gradle入門安卓筆記Gradle
- Head First ,入門首選
- 遊戲開發新手入門之DirectX入門(轉)遊戲開發
- Hadoop入門系列(2)-安裝HadoopHadoop
- Babel轉碼快速入門Babel
- [轉載] Oracle EBS 入門Oracle
- [轉]BI入門經典
- Hibernate快速入門--轉
- Oracle入門心得(2)(轉)Oracle
- redis 入門系列(轉載)Redis