2007 年 6 月 21 日
了解了 Acegi 安全系統(tǒng)(Acegi Security System)的 基礎(chǔ)知識(shí) 后,我們將介紹該系統(tǒng)的更加高級(jí)的應(yīng)用。在本文中,Bilal Siddiqui 向您展示了如何結(jié)合使用 Acegi 和一個(gè) LDAP 目錄服務(wù)器,實(shí)現(xiàn)靈活的具有高性能的 Java™ 應(yīng)用程序的安全性。還將了解如何編寫訪問控制策略并將其存儲(chǔ)在 ApacheDS 中,然后配置 Acegi 使其與目錄服務(wù)器交互,從而實(shí)現(xiàn)身份驗(yàn)證和授權(quán)的目的。
這期共分三部分的系列文章介紹了如何使用 Acegi 安全系統(tǒng)保護(hù) Java 企業(yè)應(yīng)用程序。在 本系列第一篇文章 中,我介紹了 Acegi 并解釋了如何使用安全過濾器實(shí)現(xiàn)一個(gè)簡(jiǎn)單的基于 URL 的安全系統(tǒng)。在第二篇文章中,我將討論 Acegi 的更加高級(jí)的應(yīng)用,首先我將編寫一個(gè)訪問控制策略并將其存儲(chǔ)在 ApacheDS 中,ApacheDS 是一個(gè)開源的 LDAP 目錄服務(wù)器。我還將展示配置 Acegi 的方法,使它能夠與目錄服務(wù)器交互并實(shí)現(xiàn)您的訪問控制策略。本文的結(jié)尾提供了一個(gè)示例應(yīng)用程序,它使用 ApacheDS 和 Acegi 實(shí)現(xiàn)了一個(gè)安全的訪問控制策略。
實(shí)現(xiàn)訪問控制策略通常包含兩個(gè)步驟:
- 將有關(guān)用戶和用戶角色的數(shù)據(jù)存儲(chǔ)在目錄服務(wù)器中。
- 編寫安全代碼,它將定義有權(quán)訪問并使用數(shù)據(jù)的人員。
Acegi 將減輕代碼編寫的工作,因此在這篇文章中,我將展示如何將用戶和用戶角色信息存儲(chǔ)到 ApacheDS 中,然后實(shí)現(xiàn)這些信息的訪問控制策略。在該系列的最后一篇文章中,我將展示如何配置 Acegi,實(shí)現(xiàn)對(duì) Java 類的安全訪問。
您可以在本文的任何位置 下載樣例應(yīng)用程序。參見 參考資料 下載 Acegi、Tomcat 和 ApacheDS,您需要使用它們運(yùn)行樣例代碼和示例應(yīng)用程序。
LDAP 基礎(chǔ)知識(shí)
輕量級(jí)目錄訪問協(xié)議(Lightweight Directory Access Protocol,LDAP)可能是最流行的一種定義數(shù)據(jù)格式的協(xié)議,它針對(duì)常見的目錄操作,例如對(duì)存儲(chǔ)在目錄服務(wù)器中的信息執(zhí)行的讀取、編輯、搜索和刪除操作。本節(jié)將簡(jiǎn)要解釋為什么目錄服務(wù)器是屬性文件存儲(chǔ)安全信息的首選,并展示如何在 LDAP 目錄中組織和托管用戶信息。
為什么要使用目錄服務(wù)器?
本系列第一部分向您介紹了一種簡(jiǎn)單的方法,可以將用戶信息以屬性文件的形式保存起來(lái)(參見 第 1 部分,清單 6)。屬性文件以文本格式保存用戶名、密碼和用戶角色。對(duì)于大多數(shù)真實(shí)應(yīng)用程序而言,使用屬性文件存儲(chǔ)安全信息遠(yuǎn)遠(yuǎn)不夠。各種各樣的理由表明,目錄服務(wù)器通常都是更好的選擇。其中一個(gè)原因是,真實(shí)的企業(yè)應(yīng)用程序可以被大量用戶訪問 —— 通常是幾千名用戶,如果應(yīng)用程序?qū)⑵洳糠止δ芄_給用戶和供應(yīng)商時(shí)更是如此。頻繁搜索文本文件中隨意存儲(chǔ)的信息,這樣做的效率并不高,但是目錄服務(wù)器對(duì)這類搜索進(jìn)行了優(yōu)化。
第 1 部分的清單 6 中的屬性文件演示了另一個(gè)原因,該文件組合了用戶和角色。在真實(shí)的訪問控制應(yīng)用程序中,您通常都需要分別定義和維護(hù)用戶和角色信息,這樣做可以簡(jiǎn)化用戶庫(kù)的維護(hù)。目錄服務(wù)器為更改或更新用戶信息提供了極大的靈活性,例如,反映職位升遷或新聘用人員。參見 參考資料 以了解更多關(guān)于目錄服務(wù)器的使用及其優(yōu)點(diǎn)的信息。
LDAP 目錄設(shè)置
如果希望將用戶信息存儲(chǔ)在一個(gè) LDAP 目錄中,您需要理解一些有關(guān)目錄設(shè)置的內(nèi)容。本文并沒有提供對(duì) LDAP 的完整介紹(參見 參考資料),而是介紹了一些在嘗試結(jié)合使用 Acegi 和 LDAP 目錄之前需要了解的基本概念。
LDAP 目錄以節(jié)點(diǎn)樹的形式存儲(chǔ)信息,如圖 1 所示:
圖 1. LDAP 目錄的樹狀結(jié)構(gòu)
在圖 1 中,根節(jié)點(diǎn)的名稱為 org
。根節(jié)點(diǎn)可以封裝與不同企業(yè)有關(guān)的數(shù)據(jù)。例如,本系列第 1 部分開發(fā)的制造業(yè)企業(yè)被顯示為 org
節(jié)點(diǎn)的直接子節(jié)點(diǎn)。該制造業(yè)企業(yè)具有兩個(gè)名為 departments
和 partners
的子節(jié)點(diǎn)。
partners
子節(jié)點(diǎn)封裝了不同類型的合作伙伴。圖 1 所示的三個(gè)分別為 customers
、employees
和 suppliers
。注意,這三種類型的合作伙伴其行為與企業(yè)系統(tǒng)用戶一樣。每一種類型的用戶所扮演的業(yè)務(wù)角色不同,因此訪問系統(tǒng)的權(quán)利也不同。
類似地,departments
節(jié)點(diǎn)包含該制造業(yè)企業(yè)的不同部門的數(shù)據(jù) —— 例如 engineering
和 marketing
字節(jié)點(diǎn)。每個(gè)部門節(jié)點(diǎn)還包含一組或多組用戶。在 圖 1 中,engineers
組是 engineering
部門的子節(jié)點(diǎn)。
假設(shè)每個(gè)部門的子節(jié)點(diǎn)表示一組用戶。因此,部門節(jié)點(diǎn)的子節(jié)點(diǎn)具有不同的用戶成員。例如,設(shè)計(jì)部門的所有工程師都是 engineering
部門內(nèi) engineers
組的成員。
最后,注意 圖 1 中 departments
節(jié)點(diǎn)的最后一個(gè)子節(jié)點(diǎn)。specialUser
是一名用戶,而非一組用戶。在目錄設(shè)置中,像 alice
和 bob
之類的用戶一般都包含在 partners
節(jié)點(diǎn)中。我將這個(gè)特殊用戶包含在 departments
節(jié)點(diǎn)中,以此證明 Acegi 允許用戶位于 LADP 目錄中任何地點(diǎn)的靈活性。稍后在本文中,您將了解如何配置 Acegi 以應(yīng)用 specialUser
。
使用專有名稱
LDAP 使用專有名稱(DN)的概念來(lái)識(shí)別 LDAP 樹上特定的節(jié)點(diǎn)。每個(gè)節(jié)點(diǎn)具有惟一的 DN,它包含該節(jié)點(diǎn)完整的層次結(jié)構(gòu)信息。例如,圖 2 展示了圖 1 中的一些節(jié)點(diǎn)的 DN:
圖 2. LDAP 目錄節(jié)點(diǎn)的專有名稱
首先,注意圖 2 中根節(jié)點(diǎn)的 DN。它的 DN 為 dc=org
,這是與 org
根節(jié)點(diǎn)相關(guān)的屬性值對(duì)。每個(gè)節(jié)點(diǎn)都有若干個(gè)與之相關(guān)的屬性。dc
屬性代表 “domain component” 并由 LDAP RFC 2256 定義(參見 參考資料 中有關(guān)官方 RFC 文檔的鏈接),LDAP 目錄中的根節(jié)點(diǎn)通常表示為一個(gè)域組件。
每個(gè) LDAP 屬性是由 RFC 定義的。LDAP 允許使用多個(gè)屬性創(chuàng)建一個(gè) DN,但是本文的示例只使用了以下 4 個(gè)屬性:
dc
(域組件)
o
(組織)
ou
(組織單元)
uid
(用戶 ID)
示例使用 dc
表示域,用 o
表示組織名稱,ou
表示組織的不同單元,而 uid
表示用戶。
由于 org
是根節(jié)點(diǎn),其 DN 只需指定自身的名稱(dc=org
)。比較一下,manufacturingEnterprise
節(jié)點(diǎn)的 DN 是 o=manufacturingEnterprise,dc=org
。當(dāng)向下移動(dòng)節(jié)點(diǎn)樹時(shí),每個(gè)父節(jié)點(diǎn)的 DN 被包含在其子節(jié)點(diǎn)的 DN 中。
將屬性分組
LDAP 將相關(guān)的屬性類型分到對(duì)象類中。例如,名為 organizationalPerson
的對(duì)象類所包含的屬性定義了在組織內(nèi)工作的人員(例如,職稱、常用名、郵寄地址等等)。
對(duì)象類使用繼承特性,這意味著 LDAP 定義了基類來(lái)保存常用屬性。然后子類再對(duì)基類進(jìn)行擴(kuò)展,使用其定義的屬性。LDAP 目錄中的單個(gè)節(jié)點(diǎn)可以使用若干個(gè)對(duì)象類:本文的示例使用了以下幾個(gè)對(duì)象類:
- top 對(duì)象類是 LDAP 中所有對(duì)象類的基類。
- 當(dāng)其他對(duì)象類都不適合某個(gè)對(duì)象時(shí),將使用 domain 對(duì)象類。它定義了一組屬性,任何一個(gè)屬性都可以用來(lái)指定一個(gè)對(duì)象。其
dc
屬性是強(qiáng)制性的。
- organization 對(duì)象類表示組織節(jié)點(diǎn),例如 圖 2 中的
manufacturingEnterprise
。
- organizationalUnit 對(duì)象類表示組織內(nèi)的單元,例如 圖 1 中的
departments
節(jié)點(diǎn)及其子節(jié)點(diǎn)。
- groupOfNames 對(duì)象類表示一組名稱,例如某部門職員的名稱。它具有一個(gè)
member
屬性,該屬性包含一個(gè)用戶列表。圖 1 中所有的組節(jié)點(diǎn)(例如 engineers
節(jié)點(diǎn))使用 member
屬性指定該組的成員。而且,示例使用 groupOfNames
對(duì)象類的 ou
(組織單元)屬性指定組用戶的業(yè)務(wù)角色。
- organizationalPerson 對(duì)象類表示組織內(nèi)某個(gè)職員(例如 圖 1 中的
alice
節(jié)點(diǎn))。
使用 LDAP 服務(wù)器
在真實(shí)的應(yīng)用程序中,通常將有關(guān)系統(tǒng)用戶的大量信息托管在一個(gè) LDAP 目錄中。例如,將存儲(chǔ)每個(gè)用戶的用戶名、密碼、職稱、聯(lián)系方式和工資信息。為簡(jiǎn)單起見,下面的例子將只向您展示如何保存用戶名和密碼。
如前所述,示例使用 ApacheDS(一種開源的 LDAP 目錄服務(wù)器)演示了 Acegi 是如何使用 LDAP 目錄的。示例還使用了一個(gè)開源的 LDAP 客戶機(jī)(名為 JXplorer)執(zhí)行簡(jiǎn)單的目錄操作,例如將信息托管在 ApacheDS 上。參見 參考資料 以下載 ApacheDS、JXplorer 并了解更多有關(guān)兩者協(xié)作的信息。
在 ApacheDS 創(chuàng)建一個(gè)根節(jié)點(diǎn)
要?jiǎng)?chuàng)建 圖 1 所示的節(jié)點(diǎn)樹,必須首先在 ApacheDS 中創(chuàng)建一個(gè)根節(jié)點(diǎn) org
。ApacheDS 為此提供了一個(gè) XML 配置文件。XML 配置文件定義了一組可進(jìn)行配置的 bean,從而根據(jù)應(yīng)用程序的需求定制目錄服務(wù)器的行為。本文只解釋創(chuàng)建根節(jié)點(diǎn)所需的配置。
可以在 ApacheDS 安裝中的 conf
文件夾找到名為 server.xml
的 XML 配置文件。打開文件后,會(huì)發(fā)現(xiàn)很多 bean 配置類似于 Acegi 的過濾器配置。查找名為 examplePartitionsConfiguration
的 bean。該 bean 控制 ApacheDS 上的分區(qū)。當(dāng)創(chuàng)建新的根節(jié)點(diǎn)時(shí),實(shí)際上將在 LDAP 目錄上創(chuàng)建一個(gè)新的分區(qū)。
編輯 examplePartitionConfiguration
bean 以創(chuàng)建 org
根節(jié)點(diǎn),如清單 1 所示:
清單 1. 編輯模式的 examplePartitionConfiguration bean 配置
<bean id="examplePartitionConfiguration" class=
"org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration"
>
<property name="suffix"><value>dc=org</value></property>
<property name="contextEntry">
<value>
objectClass: top
objectClass: domain
dc: org
</value>
</property>
<!-- Other properties of the examplePartitionConfiguration bean, which you don't
need to edit. -->
</bean>
|
清單 1 編輯了 examplePartitionConfiguration
bean 的兩個(gè)屬性:
- 一個(gè)屬性名為
suffix
,它定義根條目的 DN。
- 另一個(gè)屬性名為
contextEntry
,定義 org
節(jié)點(diǎn)將使用的對(duì)象類。注意,org
根節(jié)點(diǎn)使用兩個(gè)對(duì)象類:top
和 domain
。
本文的 源代碼下載 部分包含了編輯模式的 server.xml 文件。如果希望繼續(xù)學(xué)習(xí)本示例,請(qǐng)將 server.xml 文件從源代碼中復(fù)制到您的 ApacheDS 安裝目錄中的正確位置,即 conf
文件夾。
圖 3 所示的屏幕截圖展示了在 ApacheDS 中創(chuàng)建根節(jié)點(diǎn)后,JXplorer 是如何顯示該根節(jié)點(diǎn)的:
圖 3. JXplorer 顯示根節(jié)點(diǎn)
填充服務(wù)器
設(shè)置 LDAP 服務(wù)器的下一步是使用用戶和組信息填充服務(wù)器。您可以使用 JXplorer 在 ApacheDS 中逐個(gè)創(chuàng)建節(jié)點(diǎn),但是使用 LDAP Data Interchange Format (LDIF) 填充服務(wù)器會(huì)更加方便。LDIF 是可被大多數(shù) LDAP 實(shí)現(xiàn)識(shí)別的常見格式。developerWorks 文章很好地介紹了 LDIF 文件的內(nèi)容,因此本文將不再做詳細(xì)說(shuō)明。(參見 參考資料 中有關(guān) LDIF 的詳細(xì)資料。)
您可以在 源代碼下載 部分查看 LDIF 文件,它表示 圖 1 所示的用戶和部門。您可以使用 JXplorer 將 LDIF 文件導(dǎo)入到 ApacheDS。要導(dǎo)入 LDIF 文件,在 JXplorer 中使用 LDIF
菜單,如圖 4 所示:
圖 4. 將 LDIF 文件導(dǎo)入到 ApacheDS
將 LDIF 文件導(dǎo)入到 ApacheDS 之后,JXplorer 將顯示用戶節(jié)點(diǎn)和部門節(jié)點(diǎn)樹,如 圖 1 所示。現(xiàn)在您可以開始配置 Acegi,使其能夠與您的 LDAP 服務(wù)器通信。
為 LDAP 實(shí)現(xiàn)配置 Acegi
回想一下第 1 部分,其中 Acegi 使用身份驗(yàn)證處理過濾器(Authentication Processing Filter,APF)進(jìn)行身份驗(yàn)證。APF 執(zhí)行所有后端身份驗(yàn)證處理任務(wù),例如從客戶機(jī)請(qǐng)求中提取用戶名和密碼,從后端用戶庫(kù)讀取用戶參數(shù),以及使用這些信息對(duì)用戶進(jìn)行身份驗(yàn)證。
您在第 1 部分中為屬性文件實(shí)現(xiàn)配置了 APF,現(xiàn)在您已將用戶庫(kù)存儲(chǔ)在 LDAP 目錄中,因此必須使用不同的方式配置過濾器來(lái)和 LDAP 目錄進(jìn)行通信。首先看一下清單 2,它展示了在第 1 部分中的 “Authentication Processing Filter” 一節(jié)中如何為屬性文件實(shí)現(xiàn)配置 APF 過濾器:
清單 2. 為屬性文件配置 APF
<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationFailureUrl"
value="/login.jsp?login_error=1" />
<property name="defaultTargetUrl"
value="/index.jsp" />
<property name="filterProcessesUrl"
value="/j_acegi_security_check" />
</bean>
|
查看一下清單 2,您曾經(jīng)為 APF 提供了 4 個(gè)參數(shù)。您只需在 LDAP 服務(wù)器中為存儲(chǔ)重新配置第一個(gè)參數(shù)(authenticationManager
)即可。其他三個(gè)參數(shù)保持不變。
配置身份驗(yàn)證管理器
清單 3 展示了如何配置 Acegi 的身份驗(yàn)證管理器,以實(shí)現(xiàn)與 LDAP 服務(wù)器的通信:
清單 3. 為 LDAP 配置 Acegi 的身份驗(yàn)證管理器
<bean id="authenticationManager"
class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="ldapAuthenticationProvider" />
</list>
</property>
</bean>
|
在清單 3 中,org.acegisecurity.providers.ProviderManager
是一個(gè)管理器類,它管理 Acegi 的身份驗(yàn)證過程。為此,身份驗(yàn)證管理器需要一個(gè)或多個(gè)身份驗(yàn)證提供者。您可以使用管理器 bean 的提供者屬性來(lái)配置一個(gè)或多個(gè)提供者。清單 3 只包含了一個(gè)提供者,即 LDAP 身份驗(yàn)證提供者。
LDAP 身份驗(yàn)證提供者處理所有與后端 LDAP 目錄的通信。您必須對(duì)其進(jìn)行配置,下一節(jié)內(nèi)容將討論該主題。
配置 LDAP 身份驗(yàn)證提供者
清單 4 展示了 LDAP 身份驗(yàn)證提供者的配置:
清單 4. 配置 LDAP 身份驗(yàn)證提供者
<bean id="ldapAuthenticationProvider"
class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider">
<constructor-arg><ref local="authenticator"/></constructor-arg>
<constructor-arg><ref local="populator"/></constructor-arg>
</bean>
|
注意 LDAP 身份驗(yàn)證提供者類的名稱為 org.acegisecurity.providers.ldap.LdapAuthenticationProvider
。其構(gòu)造函數(shù)包含兩個(gè)參數(shù),使用兩個(gè) <constructor-arg>
標(biāo)記的形式,如清單 4 所示。
LdapAuthenticationProvider
構(gòu)造函數(shù)的第一個(gè)參數(shù)是 authenticator
,該參數(shù)通過檢查用戶的用戶名和密碼對(duì) LDAP 目錄的用戶進(jìn)行身份驗(yàn)證。完成身份驗(yàn)證后,第二個(gè)參數(shù) populator
將從 LDAP 目錄中檢索有關(guān)該用戶的訪問權(quán)限(或業(yè)務(wù)角色)信息。
以下小節(jié)將向您展示如何配置驗(yàn)證器和填充器 bean。
配置驗(yàn)證器
authenticator
bean 將檢查具有給定用戶名和密碼的用戶是否存在于 LDAP 目錄中。Acegi 提供了名為 org.acegisecurity.providers.ldap.authenticator.BindAuthenticator
的驗(yàn)證器類,它將執(zhí)行驗(yàn)證用戶名和密碼所需的功能。
配置 authenticator
bean,如清單 5 所示:
清單 5. 配置驗(yàn)證器 bean
<bean id="authenticator"
class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>
<property name="userDnPatterns">
<list>
<value>uid={0},ou=employees,ou=partners</value>
<value>uid={0},ou=customers,ou=partners</value>
<value>uid={0},ou=suppliers,ou=partners</value>
</list>
</property>
<property name="userSearch"><ref local="userSearch"/></property>
</bean>
|
在清單 5 中,BindAuthenticator
構(gòu)造函數(shù)具有一個(gè)參數(shù),使用 <constructor-arg>
標(biāo)記的形式。清單 5 中參數(shù)的名稱為 initialDirContextFactory
。該參數(shù)實(shí)際上是另一個(gè) bean,稍后您將學(xué)習(xí)如何配置該 bean。
目前為止,只知道 initialDirContextFactory
bean 的作用就是為稍后的搜索操作指定初始上下文。初始上下文是一個(gè) DN,它指定了 LDAP 目錄內(nèi)某個(gè)節(jié)點(diǎn)。指定初始上下文后,將在該節(jié)點(diǎn)的子節(jié)點(diǎn)中執(zhí)行所有的搜索操作(例如查找特定用戶)。
例如,回到 圖 2 中查看 partners
節(jié)點(diǎn),它的 DN 是 ou=partners,o=manufacturingEnterprise,dc=org
。如果將 partners
節(jié)點(diǎn)指定為初始上下文,Acegi 將只在 partners
節(jié)點(diǎn)的子節(jié)點(diǎn)中查找用戶。
指定 DN 模式
除配置 BindAuthenticator
構(gòu)造函數(shù)外,還必須配置 authenticator
bean 的兩個(gè)屬性(清單 5 中的兩個(gè) <property>
標(biāo)記)。
第一個(gè) <property>
標(biāo)記定義了一個(gè) userDnPatterns
屬性,它封裝了一個(gè)或多個(gè) DN 模式列表。DN 模式 指定了一組具有類似特性的 LDAP 節(jié)點(diǎn)(例如 圖 2 所示的 employees
節(jié)點(diǎn)的所有子節(jié)點(diǎn))。
Acegi 的身份驗(yàn)證器從 authenticator
bean 的 userDnPatterns
屬性中配置的每個(gè) DN 模式構(gòu)造了一個(gè) DN。例如,查看 清單 5 中配置的第一個(gè)模式,即 uid={0},ou=employees,ou=partners
。在進(jìn)行身份驗(yàn)證的時(shí)候,authenticator
bean 使用用戶提供的用戶名(比如 alice
)替換了 {0}
。使用用戶名取代了 {0}
之后,DN 模式將變?yōu)橄鄬?duì) DN(RDN)uid=alice,ou=employees,ou=partners
,它需要一個(gè)初始上下文才能成為 DN。
例如,查看 圖 2 中的 alice's
條目。該條目是 employees
節(jié)點(diǎn)的第一個(gè)子節(jié)點(diǎn)。它的 DN 是 uid=alice,ou=employees,ou=partners,o=manufacturingEnterprise, dc=org
。如果使用 o=manufacturingEnterprise,dc=org
作為初始上下文并將其添加到 RDN uid=alice,ou=employees,ou=partners
之后,將獲得 alice 的 DN。
使用這種方法通過 DN 模式構(gòu)建了用戶的 DN 后,authenticator
將把 DN 和用戶密碼發(fā)送到 LDAP 目錄。目錄將檢查該 DN 是否具有正確的密碼。如果有的話,用戶就可以通過身份驗(yàn)證。這個(gè)過程在 LDAP 術(shù)語(yǔ)中被稱為 bind 身份驗(yàn)證。LDAP 還提供了其他類型的身份驗(yàn)證機(jī)制,但是本文的示例只使用了 bind 身份驗(yàn)證。
如果目錄中并沒有第一個(gè) DN 模式創(chuàng)建的 DN,authenticator
bean 嘗試使用列表中配置的第二個(gè) DN 模式。依此類推,authenticator
bean 將嘗試所有的 DN 模式來(lái)為進(jìn)行身份驗(yàn)證的用戶構(gòu)造正確的用戶 DN。
搜索過濾器
回想一下較早的章節(jié) “LDAP 目錄設(shè)置”,我在將用戶信息存儲(chǔ)到 LDAP 目錄時(shí)添加了一點(diǎn)靈活性。方法是在 圖 1 所示的 departments
節(jié)點(diǎn)內(nèi)創(chuàng)建一個(gè)特定用戶(specialUser
)。
如果試圖使用 清單 5 中配置的任何一種 DN 模式創(chuàng)建特定用戶的 DN,您會(huì)發(fā)現(xiàn)沒有一種 DN 模式可用。因此,當(dāng)用戶嘗試登錄時(shí),Acegi 的 authenticator
bean 將不能夠構(gòu)造正確的 DN,從而無(wú)法對(duì)該用戶進(jìn)行身份驗(yàn)證。
通過允許您指定搜索過濾器,Acegi 能夠處理類似的特殊情況。身份驗(yàn)證器 bean 使用搜索過濾器查找不能夠通過 DN 模式構(gòu)造 DN 進(jìn)行身份驗(yàn)證的用戶。
清單 5 中的第二個(gè) <property>
標(biāo)記具有一個(gè) <ref>
子標(biāo)記,它引用名為 userSearch
的 bean。userSearch
bean 指定搜索查詢。清單 6 展示了如何配置 userSearch
bean 來(lái)處理特定用戶:
清單 6. 配置搜索查詢以搜索特定用戶
<bean id="userSearch"
class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg>
<value>ou=departments</value>
</constructor-arg>
<constructor-arg>
<value>(uid={0})</value>
</constructor-arg>
<constructor-arg>
<ref local="initialDirContextFactory" />
</constructor-arg>
<property name="searchSubtree">
<value>true</value>
</property>
</bean>
|
搜索查詢的參數(shù)
清單 6 展示了 userSearch
bean 是 org.acegisecurity.ldap.search.FilterBasedLdapUserSearch
類的一個(gè)實(shí)例,該類的構(gòu)造函數(shù)具有三個(gè)參數(shù)。第一個(gè)參數(shù)指定 authenticator
在哪個(gè)節(jié)點(diǎn)中搜索用戶。第一個(gè)參數(shù)的值為 ou=departments
,該值是一個(gè) RDN,指定了 圖 2 所示的 departments
節(jié)點(diǎn)。
第二個(gè)參數(shù) (uid={0})
指定了一個(gè)搜索過濾器。由于使用 uid
屬性指定用戶,因此可以通過查找 uid
屬性具有特定值的節(jié)點(diǎn)來(lái)查找用戶。正如您所料,花括號(hào)里面的 0 向 Acegi 表示使用進(jìn)行身份驗(yàn)證的用戶的用戶名(本例中為 specialUser
)替換 {0}
。
第三個(gè)參數(shù)是對(duì)討論 清單 5 中的 BindAuthenticator
構(gòu)造函數(shù)時(shí)引入的相同初始上下文的引用。回想一下,當(dāng)指定了初始上下文后,稍后將在該初始上下文節(jié)點(diǎn)的子節(jié)點(diǎn)內(nèi)進(jìn)行所有的搜索操作。注意,應(yīng)將指定為 清單 5 中第一個(gè)參數(shù)(ou=departments
)的值的 RDN 前加到初始上下文。
除了這三個(gè)構(gòu)造器參數(shù),清單 6 所示的 userSearch
bean 還具有一個(gè)名為 searchSubtree
的屬性。如果將其值指定為 true
,搜索操作將包括節(jié)點(diǎn)的子樹(即所有子節(jié)點(diǎn)、孫節(jié)點(diǎn)、孫節(jié)點(diǎn)的子節(jié)點(diǎn)等),該節(jié)點(diǎn)被指定為構(gòu)造函數(shù)的第一個(gè)參數(shù)的值。
authenticator
bean 的配置完成后,下一步將查看 populator
bean 的配置,如 清單 4 所示。
配置 populator
populator
bean 將讀取已經(jīng)通過 authenticator
bean 身份驗(yàn)證的用戶的業(yè)務(wù)角色。清單 7 展示 populator
bean 的 XML 配置:
清單 7. populator bean 的 XML 配置
<bean id="populator"
class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
<constructor-arg>
<ref local="initialDirContextFactory"/>
</constructor-arg>
<constructor-arg>
<value>ou=departments</value>
</constructor-arg>
<property name="groupRoleAttribute">
<value>ou</value>
</property>
<property name="searchSubtree">
<value>true</value>
</property>
</bean>
|
在清單 7 中,populator
bean 的構(gòu)造函數(shù)包括 2 個(gè)參數(shù),以及一個(gè) groupRoleAttribute
屬性。構(gòu)造函數(shù)的第一個(gè)參數(shù)指定了 populator
bean 用來(lái)讀取經(jīng)過驗(yàn)證用戶的業(yè)務(wù)角色的初始上下文。并不強(qiáng)制要求 authenticator
和 populator
bean 使用相同的初始上下文。您可以為這兩者分別配置一個(gè)初始上下文。
第二個(gè)構(gòu)造函數(shù)參數(shù)指定了 populator 前加到初始上下文的 RDN。因此,RDN 組成了包含組用戶的節(jié)點(diǎn)的 DN,例如 departments
節(jié)點(diǎn)。
populator
bean 的 groupRoleAttribute
屬性指定了持有組成員業(yè)務(wù)角色數(shù)據(jù)的屬性。回想 設(shè)置 LDAP 目錄 一節(jié)中,您將每組用戶的業(yè)務(wù)角色信息存儲(chǔ)在名為 ou
的屬性中。然后將 ou
設(shè)置為 groupRoleAttribute
屬性的值,如 清單 7 所示。
如您所料,populator
bean 將搜索整個(gè) LDAP 目錄來(lái)查找經(jīng)過驗(yàn)證的用戶所屬的組節(jié)點(diǎn)。然后讀取組節(jié)點(diǎn)的 ou
屬性的值,獲取用戶經(jīng)過授權(quán)的業(yè)務(wù)角色。
這樣就完成了 populator
bean 的配置。目前為止,我們?cè)谌齻€(gè)位置使用了初始上下文:清單 5、清單 6 和 清單 7。接下來(lái)將了解如何配置初始上下文。
配置初始上下文
清單 8 展示了在 Acegi 中配置初始上下文的過程:
清單 8. 初始上下文的 XML 配置
<bean id="initialDirContextFactory"
class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">
<constructor-arg value="ldap://localhost:389/o=manufacturingEnterprise,dc=org"/>
<property name="managerDn">
<value>cn=manager,o=manufacturingEnterprise,dc=org</value>
</property>
<property name="managerPassword">
<value>secret</value>
</property>
</bean>
|
清單 8 中 Acegi 的初始上下文類的名稱為 org.acegisecurity.ldap.DefaultInitialDirContextFactory
,這是 Acegi 包含的工廠類。Acegi 在內(nèi)部使用該類構(gòu)造其他處理目錄操作(例如在整個(gè)目錄中搜索)的類的對(duì)象。當(dāng)配置初始上下文工廠時(shí),必須指定以下內(nèi)容:
- 將您的 LDAP 目錄和根目錄節(jié)點(diǎn)的網(wǎng)絡(luò)地址指定為構(gòu)造函數(shù)的參數(shù)。在初始上下文配置的節(jié)點(diǎn)將作為根節(jié)點(diǎn)。就是說(shuō)所有后續(xù)操作(例如
search
)都將在根節(jié)點(diǎn)定義的子樹中執(zhí)行。
- 將 DN 和密碼分別定義為
managerDn
和 managerPassword
屬性。在執(zhí)行任何搜索操作之前,Acegi 必須使用目錄服務(wù)器對(duì) DN 和密碼進(jìn)行身份驗(yàn)證。
您已經(jīng)了解了如何將用戶庫(kù)托管在 LDAP 目錄中,以及如何配置 Acegi 來(lái)使用來(lái)自 LDAP 目錄的信息對(duì)用戶進(jìn)行身份驗(yàn)證。下一節(jié)將進(jìn)一步介紹 Acegi 的身份驗(yàn)證處理過濾器,了解新配置的 bean 是如何管理身份驗(yàn)證過程的。
身份驗(yàn)證和授權(quán)
APF 配置完成后,將能夠與 LDAP 目錄進(jìn)行通信來(lái)對(duì)用戶進(jìn)行身份驗(yàn)證。如果您閱讀過第 1 部分,那么對(duì)與目錄通信過程中 APF 執(zhí)行的一些步驟不會(huì)感到陌生,我在第 1 部分中向您展示了過濾器如何使用不同的服務(wù)進(jìn)行用戶身份驗(yàn)證。圖 5 所示的序列表與您在 第 1 部分圖 3 看到的非常類似:
圖 5. APF 對(duì)一名 LDAP 用戶進(jìn)行身份驗(yàn)證
無(wú)論 APF 使用屬性文件進(jìn)行內(nèi)部的身份驗(yàn)證還是與 LDAP 服務(wù)器進(jìn)行通信,步驟 1 到步驟 9 與第 1 部分是相同的。這里簡(jiǎn)單描述了前 9 個(gè)步驟,您可以從步驟 10 開始繼續(xù)學(xué)習(xí)特定于 LDAP 的事件:
- 過濾器鏈前面的過濾器將請(qǐng)求、響應(yīng)和過濾器鏈對(duì)象傳遞給 APF。
- APF 使用取自請(qǐng)求對(duì)象的用戶名、密碼和其他信息創(chuàng)建一個(gè)身份驗(yàn)證標(biāo)記。
- APF 將身份驗(yàn)證標(biāo)記傳遞給身份驗(yàn)證管理器。
- 身份驗(yàn)證管理器可能包含一個(gè)或多個(gè)身份驗(yàn)證提供者。每個(gè)提供者恰好支持一種身份驗(yàn)證類型。管理器將檢查哪一種提供者支持從 APF 接收到的身份驗(yàn)證標(biāo)記。
- 身份驗(yàn)證管理器將身份驗(yàn)證標(biāo)記傳遞給適合該類型身份驗(yàn)證的提供者。
- 身份驗(yàn)證提供者從身份驗(yàn)證標(biāo)記中提取用戶名并將其傳遞到名為 user cache service 的服務(wù)。Acegi 緩存了已經(jīng)進(jìn)行過身份驗(yàn)證的用戶。該用戶下次登錄時(shí),Acegi 可以從緩存中加載他或她的詳細(xì)信息(比如用戶名、密碼和權(quán)限),而不是從后端數(shù)據(jù)存儲(chǔ)中讀取數(shù)據(jù)。這種方法使得性能得到了改善。
- user cache service 檢查用戶的詳細(xì)信息是否存在于緩存中。
- user cache service 將用戶的詳細(xì)信息返回給身份驗(yàn)證提供者。如果緩存不包含用戶詳細(xì)信息,則返回 null。
- 身份驗(yàn)證提供者檢查緩存服務(wù)返回的是用戶的詳細(xì)信息還是 null。
- 從這里開始,身份驗(yàn)證處理將特定于 LDAP。 如果緩存返回 null,LDAP 身份驗(yàn)證提供者將把用戶名(在步驟 6 中提取的)和密碼傳遞給 清單 5 中配置的
authenticator
bean。
authenticator
將使用在 清單 5 的 userDnPatterns
屬性中配置的 DN 模式創(chuàng)建用戶 DN。通過從一個(gè) DN 模式中創(chuàng)建一個(gè) DN,然后將該 DN 和用戶密碼(從用戶請(qǐng)求中獲得)發(fā)送到 LDAP 目錄,它將逐一嘗試所有可用的 DN 模式。LDAP 目錄將檢查該 DN 是否存在以及密碼是否正確。如果其中任何一個(gè) DN 模式可行的話,用戶被綁定到 LDAP 目錄中,authenticator
將繼續(xù)執(zhí)行步驟 15。
- 如果任何一種 DN 模式都不能工作的話(這意味著在 DN 模式指定的任何位置都不存在使用給定密碼的用戶),
authenticator
根據(jù) 清單 6 配置的搜索查詢?cè)?LDAP 目錄中搜索用戶。如果 LDAP 目錄沒有找到用戶,那么身份驗(yàn)證以失敗告終。
- 如果 LDAP 目錄查找到了用戶,它將用戶的 DN 返回到
authenticator
。
authenticator
將用戶 DN 和密碼發(fā)送到 LDAP 目錄來(lái)檢查用戶密碼是否正確。如果 LDAP 目錄發(fā)現(xiàn)用戶密碼是正確的,該用戶將被綁定到 LDAP 目錄。
authenticator
將用戶信息發(fā)送回 LDAP 身份驗(yàn)證提供者。
- LDAP 身份驗(yàn)證提供者將控制權(quán)傳遞給
populator
bean。
populator
搜索用戶所屬的組。
- LDAP 目錄將用戶角色信息返回給
populator
。
populator
將用戶角色信息返回給 LDAP 身份驗(yàn)證提供者。
- LDAP 身份驗(yàn)證提供者將用戶的詳細(xì)信息(以及用戶業(yè)務(wù)角色信息)返回給 APF。用戶現(xiàn)在成功進(jìn)行了身份驗(yàn)證。
不論使用何種身份驗(yàn)證方法,最后三個(gè)步驟是相同的(步驟21、21 和 23)。
配置攔截器
您已經(jīng)了解了 APF 對(duì)用戶進(jìn)行身份驗(yàn)證的步驟。接下來(lái)是查看成功進(jìn)行身份驗(yàn)證的用戶是否被授權(quán)訪問所請(qǐng)求的資源。這項(xiàng)任務(wù)由 Acegi 的攔截過濾器(Interceptor Filter,IF)完成。本節(jié)將向您展示如何配置 IF 來(lái)實(shí)現(xiàn)訪問控制策略。
回想一下在 第 1 部分的清單 7 中配置 IF 的步驟。攔截過濾器在資源和角色之間建立映射,就是說(shuō)只有具備必要角色的用戶才能訪問給定資源。為了演示制造業(yè)企業(yè)中不同部門的業(yè)務(wù)角色,清單 9 向現(xiàn)有的 IF 配置添加了另外的角色:
清單 9. 配置攔截過濾器
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="accessDecisionManager" />
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/protected/engineering/**=ROLE_HEAD_OF_ENGINEERING
/protected/marketing/**=ROLE_HEAD_OF_MARKETING
/**=IS_AUTHENTICATED_ANONYMOUSLY
</value>
</property>
</bean>
|
在清單 9 中,IF 包含三個(gè)參數(shù)。其中第一個(gè)和第三個(gè)參數(shù)與第 1 部分中最初配置的參數(shù)相同。這里添加了第二個(gè)參數(shù)(名為 accessDecisionManager
的 bean)。
accessDecisionManager
bean 負(fù)責(zé)指定授權(quán)決策。它使用清單 9 中第三個(gè)參數(shù)提供的訪問控制定義來(lái)指定授權(quán)(或訪問控制)決策。第三個(gè)參數(shù)是 objectDefinitionSource
。
配置訪問決策管理器
accessDecisionManager
決定是否允許一個(gè)用戶訪問某個(gè)資源。Acegi 提供了一些訪問決策管理器,它們指定訪問控制決策的方式有所不同。本文只解釋了其中一種訪問決策管理器的工作方式,其配置如清單 10 所示:
清單 10. 配置訪問決策管理器
<bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<bean class="org.acegisecurity.vote.RoleVoter"/>
<bean class="org.acegisecurity.vote.AuthenticatedVoter" />
</list>
</property>
</bean>
|
在清單 10 中,accessDecisionManager
bean 是 org.acegisecurity.vote.AffirmativeBased
類的實(shí)例。accessDecisionManager
bean 只包含一個(gè)參數(shù),即投票者(voter)列表。
在 Acegi 中,投票者確定是否允許某個(gè)用戶訪問特定的資源。當(dāng)使用 accessDecisionManager
查詢時(shí),投票者具有三個(gè)選項(xiàng):允許訪問(access-granted)、拒絕訪問(access-denied),如果不確定的話則放棄投票(abstain from voting)。
不同類型的訪問決策管理器解釋投票者決策的方法也有所不同。清單 10 所示的 AffirmativeBased
訪問決策管理器實(shí)現(xiàn)了簡(jiǎn)單的決策邏輯:如果任何投票者強(qiáng)制執(zhí)行肯定投票,將允許用戶訪問所請(qǐng)求的資源。
投票者邏輯
Acegi 提供了若干個(gè)投票者實(shí)現(xiàn)類型。accessDecisionManager
將經(jīng)過驗(yàn)證的用戶的信息(包括用戶的業(yè)務(wù)角色信息)和 objectDefinitionSource
對(duì)象傳遞給投票者。本文的示例使用了兩種類型的投票者,RoleVoter
和 AuthenticatedVoter
,如清單 10 所示。現(xiàn)在看一下每種投票者的邏輯:
- RoleVoter 只有在
objectDefinitionSource
對(duì)象的行中找到以 ROLE_
前綴開頭的角色時(shí)才進(jìn)行投票。如果 RoleVoter
沒有找到這樣的行,將放棄投票;如果在用戶業(yè)務(wù)角色中找到一個(gè)匹配的角色,它將投票給允許訪問;如果沒有找到匹配的角色,則投票給拒絕訪問。在 清單 9 中,有兩個(gè)角色具有 ROLE_
前綴:ROLE_HEAD_OF_ENGINEERING
和 ROLE_HEAD_OF_MARKETING
。
- AuthenticatedVoter 只有在
objectDefinitionSource
對(duì)象中找到具有某個(gè)預(yù)定義角色的行時(shí)才進(jìn)行投票。在 清單 9 中,有這樣一行:IS_AUTHENTICATED_ANONYMOUSLY
。匿名身份驗(yàn)證意味著用戶不能夠進(jìn)行身份驗(yàn)證。找到該行后,AuthenticatedVoter
將檢查一個(gè)匿名身份驗(yàn)證的用戶是否可以訪問某些不受保護(hù)的資源(即這些資源沒有包含在具備 ROLE_
前綴的行中)。如果 AuthenticatedVoter
發(fā)現(xiàn)所請(qǐng)求的資源是不受保護(hù)的并且 objectDefinitionSource
對(duì)象允許匿名身份驗(yàn)證的用戶訪問不受保護(hù)的資源,它將投票給允許訪問;否則就投票給拒絕訪問。
示例應(yīng)用程序
本文提供了一個(gè)示例應(yīng)用程序,它將演示您目前掌握的 LDAP 和 Acegi 概念。LDAP-Acegi 應(yīng)用程序?qū)@示一個(gè)索引頁(yè)面,該頁(yè)面將設(shè)計(jì)和銷售文檔呈現(xiàn)給合適的經(jīng)過身份驗(yàn)證的用戶。正如您將看到的一樣,LDAP-Acegi 應(yīng)用程序允許用戶 alice
查看設(shè)計(jì)文檔,并允許用戶 bob
查看銷售文檔。它還允許特定用戶同時(shí)查看設(shè)計(jì)和銷售文檔。所有這些內(nèi)容都是在本文開頭配置 LDAP 目錄服務(wù)器時(shí)設(shè)置的。立即 下載示例應(yīng)用程序 來(lái)開始使用它。
結(jié)束語(yǔ)
在本文中,您了解了如何將用戶和業(yè)務(wù)角色信息托管在 LDAP 目錄中。您還詳細(xì)了解了配置 Acegi 的方法,從而與 LDAP 目錄交互實(shí)現(xiàn)訪問控制策略。在本系列最后一期文章中,我將展示如何配置 Acegi 來(lái)保護(hù)對(duì) Java 類的訪問。
來(lái)自:http://www.cnblogs.com/amboyna/archive/2008/03/25/1122084.html