AD & ADSI入門 (轉)

amyz發表於2007-08-16
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。
[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
 
  • 擴充套件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各有一些特定的必備屬性。
 
  擴充套件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="
  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
 
  ' 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.Creatbox("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 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
 
 
PS:很久以前寫的東西,望指正。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-962353/,如需轉載,請註明出處,否則將追究法律責任。

AD & ADSI入門 (轉)
請登入後發表評論 登入
全部評論

相關文章