程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> .NET實例教程 >> AD 和 ADSI 入門知識

AD 和 ADSI 入門知識

編輯:.NET實例教程


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

    本文來自“十萬個為什麼”電腦學習網 http://www.why100000.com

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved