程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 使用Acegi保護Java應用程序,第2部分: 使用LDAP目錄服務器

使用Acegi保護Java應用程序,第2部分: 使用LDAP目錄服務器

編輯:關於JAVA

使用 ApacheDS 和 Acegi 實現訪問控制

了解了 Acegi 安全系統(Acegi Security System)的 基礎知識 後,我們 將介紹該系統的更加高級的應用。在本文中,Bilal Siddiqui 向您展示了如何 結合使用 Acegi 和一個 LDAP 目錄服務器,實現靈活的具有高性能的 Java™ 應用程序的安全性。還將了解如何編寫訪問控制策略並將其存儲在 ApacheDS 中,然後配置 Acegi 使其與目錄服務器交互,從而實現身份驗證和授 權的目的。

這期共分三部分的系列文章介紹了如何使用 Acegi 安全系統保護 Java 企業 應用程序。在 本系列第一篇文章 中,我介紹了 Acegi 並解釋了如何使用安全 過濾器實現一個簡單的基於 URL 的安全系統。在第二篇文章中,我將討論 Acegi 的更加高級的應用,首先我將編寫一個訪問控制策略並將其存儲在 ApacheDS 中,ApacheDS 是一個開源的 LDAP 目錄服務器。我還將展示配置 Acegi 的方法,使它能夠與目錄服務器交互並實現您的訪問控制策略。本文的結 尾提供了一個示例應用程序,它使用 ApacheDS 和 Acegi 實現了一個安全的訪 問控制策略。

實現訪問控制策略通常包含兩個步驟:

將有關用戶 和用戶角色的數據存儲在目錄服務器中。

編寫安全代碼,它將定義有權 訪問並使用數據的人員。

Acegi 將減輕代碼編寫的工作,因此在這篇文 章中,我將展示如何將用戶和用戶角色信息存儲到 ApacheDS 中,然後實現這些 信息的訪問控制策略。在該系列的最後一篇文章中,我將展示如何配置 Acegi, 實現對 Java 類的安全訪問。

LDAP 基礎知識

輕量級目錄訪問協 議(Lightweight Directory Access Protocol,LDAP)可能是最流行的一種定 義數據格式的協議,它針對常見的目錄操作,例如對存儲在目錄服務器中的信息 執行的讀取、編輯、搜索和刪除操作。本節將簡要解釋為什麼目錄服務器是屬性 文件存儲安全信息的首選,並展示如何在 LDAP 目錄中組織和托管用戶信息。

為什麼要使用目錄服務器?

本系列第一部分向您介紹了一種簡單的方法,可以將用戶信息以屬性文件的 形式保存起來(參見 第 1 部分,清單 6)。屬性文件以文本格式保存用戶名、 密碼和用戶角色。對於大多數真實應用程序而言,使用屬性文件存儲安全信息遠 遠不夠。各種各樣的理由表明,目錄服務器通常都是更好的選擇。其中一個原因 是,真實的企業應用程序可以被大量用戶訪問 —— 通常是幾千名用戶,如果應 用程序將其部分功能公開給用戶和供應商時更是如此。頻繁搜索文本文件中隨意 存儲的信息,這樣做的效率並不高,但是目錄服務器對這類搜索進行了優化。

第 1 部分的清單 6 中的屬性文件演示了另一個原因,該文件組合了用戶和 角色。在真實的訪問控制應用程序中,您通常都需要分別定義和維護用戶和角色 信息,這樣做可以簡化用戶庫的維護。目錄服務器為更改或更新用戶信息提供了 極大的靈活性,例如,反映職位升遷或新聘用人員。

LDAP 目錄設置

如果希望將用戶信息存儲在一個 LDAP 目錄中,您需要理解一些有關目錄設 置的內容。本文並沒有提供對 LDAP 的完整介紹,而是介紹了一些在嘗試結合使 用 Acegi 和 LDAP 目錄之前需要了解的基本概念。

LDAP 目錄以節點樹的形式存儲信息,如圖 1 所示:

圖 1. LDAP 目錄的樹狀結構

在圖 1 中,根節點的名稱為 org。根節點可以封裝與不同企業有關的數據。 例如,本系列第 1 部分開發的制造業企業被顯示為 org 節點的直接子節點。該 制造業企業具有兩個名為 departments 和 partners 的子節點。

partners 子節點封裝了不同類型的合作伙伴。圖 1 所示的三個分別為 customers、employees 和 suppliers。注意,這三種類型的合作伙伴其行為與 企業系統用戶一樣。每一種類型的用戶所扮演的業務角色不同,因此訪問系統的 權利也不同。

類似地,departments 節點包含該制造業企業的不同部門的數據 —— 例如 engineering 和 marketing 字節點。每個部門節點還包含一組或多組用戶。在 圖 1 中,engineers 組是 engineering 部門的子節點。

假設每個部門的子節點表示一組用戶。因此,部門節點的子節點具有不同的 用戶成員。例如,設計部門的所有工程師都是 engineering 部門內 engineers 組的成員。

最後,注意 圖 1 中 departments 節點的最後一個子節點。specialUser 是 一名用戶,而非一組用戶。在目錄設置中,像 alice 和 bob 之類的用戶一般都 包含在 partners 節點中。我將這個特殊用戶包含在 departments 節點中,以 此證明 Acegi 允許用戶位於 LADP 目錄中任何地點的靈活性。稍後在本文中, 您將了解如何配置 Acegi 以應用 specialUser。

使用專有名稱

LDAP 使用專有名稱(DN)的概念來識別 LDAP 樹上特定的節點。每個節點具 有惟一的 DN,它包含該節點完整的層次結構信息。例如,圖 2 展示了圖 1 中 的一些節點的 DN:

圖 2. LDAP 目錄節點的專有名稱

首先,注意圖 2 中根節點的 DN。它的 DN 為 dc=org,這是與 org 根節點 相關的屬性值對。每個節點都有若干個與之相關的屬性。dc 屬性代表 “domain component” 並由 LDAP RFC 2256 定義,LDAP 目錄中的根節點通常表示為一個 域組件。

每個 LDAP 屬性是由 RFC 定義的。LDAP 允許使用多個屬性創建一個 DN,但 是本文的示例只使用了以下 4 個屬性:

dc(域組件)

o(組織)

ou(組織單元)

uid(用戶 ID)

示例使用 dc 表示域,用 o 表示組織名稱,ou 表示組織的不同單元,而 uid 表示用戶。

由於 org 是根節點,其 DN 只需指定自身的名稱(dc=org)。比較一下, manufacturingEnterprise 節點的 DN 是 o=manufacturingEnterprise,dc=org 。當向下移動節點樹時,每個父節點的 DN 被包含在其子節點的 DN 中。

將屬性分組

LDAP 將相關的屬性類型分到對象類中。例如,名為 organizationalPerson 的對象類所包含的屬性定義了在組織內工作的人員(例如,職稱、常用名、郵寄 地址等等)。

對象類使用繼承特性,這意味著 LDAP 定義了基類來保存常用屬性。然後子 類再對基類進行擴展,使用其定義的屬性。LDAP 目錄中的單個節點可以使用若 干個對象類:本文的示例使用了以下幾個對象類:

top 對象類是 LDAP 中所有對象類的基類。

當其他對象類都不適合某個對象時,將使用 domain 對象類。它定義了一組 屬性,任何一個屬性都可以用來指定一個對象。其 dc 屬性是強制性的。

organization 對象類表示組織節點,例如 圖 2 中的 manufacturingEnterprise。

organizationalUnit 對象類表示組織內的單元,例如 圖 1 中的 departments 節點及其子節點。

groupOfNames 對象類表示一組名稱,例如某部門職員的名稱。它具有一個 member 屬性,該屬性包含一個用戶列表。圖 1 中所有的組節點(例如 engineers 節點)使用 member 屬性指定該組的成員。而且,示例使用 groupOfNames 對象類的 ou(組織單元)屬性指定組用戶的業務角色。

organizationalPerson 對象類表示組織內某個職員(例如 圖 1 中的 alice 節點)。

使用 LDAP 服務器

在真實的應用程序中,通常將有關系統用戶的大量信息托管在一個 LDAP 目 錄中。例如,將存儲每個用戶的用戶名、密碼、職稱、聯系方式和工資信息。為 簡單起見,下面的例子將只向您展示如何保存用戶名和密碼。

如前所述,示例使用 ApacheDS(一種開源的 LDAP 目錄服務器)演示了 Acegi 是如何使用 LDAP 目錄的。示例還使用了一個開源的 LDAP 客戶機(名為 JXplorer)執行簡單的目錄操作,例如將信息托管在 ApacheDS 上。

在 ApacheDS 創建一個根節點

要創建 圖 1 所示的節點樹,必須首先在 ApacheDS 中創建一個根節點 org 。ApacheDS 為此提供了一個 XML 配置文件。XML 配置文件定義了一組可進行配 置的 bean,從而根據應用程序的需求定制目錄服務器的行為。本文只解釋創建 根節點所需的配置。

可以在 ApacheDS 安裝中的 conf 文件夾找到名為 server.xml 的 XML 配置 文件。打開文件後,會發現很多 bean 配置類似於 Acegi 的過濾器配置。查找 名為 examplePartitionsConfiguration 的 bean。該 bean 控制 ApacheDS 上 的分區。當創建新的根節點時,實際上將在 LDAP 目錄上創建一個新的分區。

編輯 examplePartitionConfiguration bean 以創建 org 根節點,如清單 1 所示:

清單 1. 編輯模式的 examplePartitionConfiguration bean 配置

<bean id="examplePartitionConfiguration" class=
"org.apache.directory.server.core.partition.impl.btree.MutableBTreePar titionConfiguration"
>

<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 的兩個屬性:

一個屬性名為 suffix,它定義根條目的 DN。

另一個屬性名為 contextEntry,定義 org 節點將使用的對象類。注意,org 根節點使用兩個對象類:top 和 domain。

本文的 源代碼下載 部分包含了編輯模式的 server.xml 文件。如果希望繼 續學習本示例,請將 server.xml 文件從源代碼中復制到您的 ApacheDS 安裝目 錄中的正確位置,即 conf 文件夾。

圖 3 所示的屏幕截圖展示了在 ApacheDS 中創建根節點後,JXplorer 是如 何顯示該根節點的:

圖 3. JXplorer 顯示根節點

填充服務器

設置 LDAP 服務器的下一步是使用用戶和組信息填充服務器。您可以使用 JXplorer 在 ApacheDS 中逐個創建節點,但是使用 LDAP Data Interchange Format (LDIF) 填充服務器會更加方便。LDIF 是可被大多數 LDAP 實現識別的 常見格式。developerWorks 文章很好地介紹了 LDIF 文件的內容,因此本文將 不再做詳細說明。

您可以在 源代碼下載 部分查看 LDIF 文件,它表示 圖 1 所示的用戶和部 門。您可以使用 JXplorer 將 LDIF 文件導入到 ApacheDS。要導入 LDIF 文件 ,在 JXplorer 中使用 LDIF 菜單,如圖 4 所示:

圖 4. 將 LDIF 文件導入到 ApacheDS

將 LDIF 文件導入到 ApacheDS 之後,JXplorer 將顯示用戶節點和部門節點 樹,如 圖 1 所示。現在您可以開始配置 Acegi,使其能夠與您的 LDAP 服務器 通信。

為 LDAP 實現配置 Acegi

回想一下第 1 部分,其中 Acegi 使用身份驗證處理過濾器 (Authentication Processing Filter,APF)進行身份驗證。APF 執行所有後 端身份驗證處理任務,例如從客戶機請求中提取用戶名和密碼,從後端用戶庫讀 取用戶參數,以及使用這些信息對用戶進行身份驗證。

您在第 1 部分中為屬性文件實現配置了 APF,現在您已將用戶庫存儲在 LDAP 目錄中,因此必須使用不同的方式配置過濾器來和 LDAP 目錄進行通信。 首先看一下清單 2,它展示了在第 1 部分中的 “Authentication Processing Filter” 一節中如何為屬性文件實現配置 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,您曾經為 APF 提供了 4 個參數。您只需在 LDAP 服務器 中為存儲重新配置第一個參數(authenticationManager)即可。其他三個參數 保持不變。

配置身份驗證管理器

清單 3 展示了如何配置 Acegi 的身份驗證管理器,以實現與 LDAP 服務器 的通信:

清單 3. 為 LDAP 配置 Acegi 的身份驗證管理器

<bean id="authenticationManager"
class="org.acegisecurity.providers.ProviderManager">

<property name="providers">
<list>
<ref local="ldapAuthenticationProvider" />
</list>
</property>

</bean>

在清單 3 中,org.acegisecurity.providers.ProviderManager 是一個管理 器類,它管理 Acegi 的身份驗證過程。為此,身份驗證管理器需要一個或多個 身份驗證提供者。您可以使用管理器 bean 的提供者屬性來配置一個或多個提供 者。清單 3 只包含了一個提供者,即 LDAP 身份驗證提供者。

LDAP 身份驗證提供者處理所有與後端 LDAP 目錄的通信。您必須對其進行配 置,下一節內容將討論該主題。

配置 LDAP 身份驗證提供者

清單 4 展示了 LDAP 身份驗證提供者的配置:

清單 4. 配置 LDAP 身份驗證提供者

<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 身份驗證提供者類的名稱為 org.acegisecurity.providers.ldap.LdapAuthenticationProvider 。其構造函 數包含兩個參數,使用兩個 <constructor-arg> 標記的形式,如清單 4 所示。

LdapAuthenticationProvider 構造函數的第一個參數是 authenticator,該 參數通過檢查用戶的用戶名和密碼對 LDAP 目錄的用戶進行身份驗證。完成身份 驗證後,第二個參數 populator 將從 LDAP 目錄中檢索有關該用戶的訪問權限 (或業務角色)信息。

以下小節將向您展示如何配置驗證器和填充器 bean。

配置驗證器

authenticator bean 將檢查具有給定用戶名和密碼的用戶是否存在於 LDAP 目錄中。Acegi 提供了名為 org.acegisecurity.providers.ldap.authenticator.BindAuthenticator 的驗 證器類,它將執行驗證用戶名和密碼所需的功能。

配置 authenticator bean,如清單 5 所示:

清單 5. 配置驗證器 bean

<bean id="authenticator"
class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticato r">

<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 構造函數具有一個參數,使用 <constructor-arg> 標記的形式。清單 5 中參數的名稱為 initialDirContextFactory。該參數實際上是另一個 bean,稍後您將學習如何 配置該 bean。

目前為止,只知道 initialDirContextFactory bean 的作用就是為稍後的搜 索操作指定初始上下文。初始上下文是一個 DN,它指定了 LDAP 目錄內某個節 點。指定初始上下文後,將在該節點的子節點中執行所有的搜索操作(例如查找 特定用戶)。

例如,回到 圖 2 中查看 partners 節點,它的 DN 是 ou=partners,o=manufacturingEnterprise,dc=org。如果將 partners 節點指定 為初始上下文,Acegi 將只在 partners 節點的子節點中查找用戶。

指定 DN 模式

除配置 BindAuthenticator 構造函數外,還必須配置 authenticator bean 的兩個屬性(清單 5 中的兩個 <property> 標記)。

第一個 <property> 標記定義了一個 userDnPatterns 屬性,它封裝 了一個或多個 DN 模式列表。DN 模式 指定了一組具有類似特性的 LDAP 節點( 例如 圖 2 所示的 employees 節點的所有子節點)。

Acegi 的身份驗證器從 authenticator bean 的 userDnPatterns 屬性中配 置的每個 DN 模式構造了一個 DN。例如,查看 清單 5 中配置的第一個模式, 即 uid={0},ou=employees,ou=partners。在進行身份驗證的時候, authenticator bean 使用用戶提供的用戶名(比如 alice)替換了 {0}。使用 用戶名取代了 {0} 之後,DN 模式將變為相對 DN(RDN) uid=alice,ou=employees,ou=partners,它需要一個初始上下文才能成為 DN。

例如,查看 圖 2 中的 alice's 條目。該條目是 employees 節點的第一個 子節點。它的 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 模式構建了用戶的 DN 後,authenticator 將把 DN 和用戶密碼發送到 LDAP 目錄。目錄將檢查該 DN 是否具有正確的密碼。如果有 的話,用戶就可以通過身份驗證。這個過程在 LDAP 術語中被稱為 bind 身份驗 證。LDAP 還提供了其他類型的身份驗證機制,但是本文的示例只使用了 bind 身份驗證。

如果目錄中並沒有第一個 DN 模式創建的 DN,authenticator bean 嘗試使 用列表中配置的第二個 DN 模式。依此類推,authenticator bean 將嘗試所有 的 DN 模式來為進行身份驗證的用戶構造正確的用戶 DN。

搜索過濾器

回想一下較早的章節 “LDAP 目錄設置”,我在將用戶信息存儲到 LDAP 目 錄時添加了一點靈活性。方法是在 圖 1 所示的 departments 節點內創建一個 特定用戶(specialUser)。

如果試圖使用 清單 5 中配置的任何一種 DN 模式創建特定用戶的 DN,您會 發現沒有一種 DN 模式可用。因此,當用戶嘗試登錄時,Acegi 的 authenticator bean 將不能夠構造正確的 DN,從而無法對該用戶進行身份驗證 。

通過允許您指定搜索過濾器,Acegi 能夠處理類似的特殊情況。身份驗證器 bean 使用搜索過濾器查找不能夠通過 DN 模式構造 DN 進行身份驗證的用戶。

清單 5 中的第二個 <property> 標記具有一個 <ref> 子標記 ,它引用名為 userSearch 的 bean。userSearch bean 指定搜索查詢。清單 6 展示了如何配置 userSearch bean 來處理特定用戶:

清單 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>

搜索查詢的參數

清單 6 展示了 userSearch bean 是 org.acegisecurity.ldap.search.FilterBasedLdapUserSearch 類的一個實例, 該類的構造函數具有三個參數。第一個參數指定 authenticator 在哪個節點中 搜索用戶。第一個參數的值為 ou=departments,該值是一個 RDN,指定了 圖 2 所示的 departments 節點。

第二個參數 (uid={0}) 指定了一個搜索過濾器。由於使用 uid 屬性指定用 戶,因此可以通過查找 uid 屬性具有特定值的節點來查找用戶。正如您所料, 花括號裡面的 0 向 Acegi 表示使用進行身份驗證的用戶的用戶名(本例中為 specialUser)替換 {0}。

第三個參數是對討論 清單 5 中的 BindAuthenticator 構造函數時引入的相 同初始上下文的引用。回想一下,當指定了初始上下文後,稍後將在該初始上下 文節點的子節點內進行所有的搜索操作。注意,應將指定為 清單 5 中第一個參 數(ou=departments)的值的 RDN 前加到初始上下文。

除了這三個構造器參數,清單 6 所示的 userSearch bean 還具有一個名為 searchSubtree 的屬性。如果將其值指定為 true,搜索操作將包括節點的子樹 (即所有子節點、孫節點、孫節點的子節點等),該節點被指定為構造函數的第 一個參數的值。

authenticator bean 的配置完成後,下一步將查看 populator bean 的配置 ,如 清單 4 所示。

配置 populator

populator bean 將讀取已經通過 authenticator bean 身份驗證的用戶的業 務角色。清單 7 展示 populator bean 的 XML 配置:

清單 7. populator bean 的 XML 配置

<bean id="populator"
class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoriti esPopulator">

<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 的構造函數包括 2 個參數,以及一個 groupRoleAttribute 屬性。構造函數的第一個參數指定了 populator bean 用 來讀取經過驗證用戶的業務角色的初始上下文。並不強制要求 authenticator 和 populator bean 使用相同的初始上下文。您可以為這兩者分別配置一個初始 上下文。

第二個構造函數參數指定了 populator 前加到初始上下文的 RDN。因此, RDN 組成了包含組用戶的節點的 DN,例如 departments 節點。

populator bean 的 groupRoleAttribute 屬性指定了持有組成員業務角色數 據的屬性。回想 設置 LDAP 目錄 一節中,您將每組用戶的業務角色信息存儲在 名為 ou 的屬性中。然後將 ou 設置為 groupRoleAttribute 屬性的值,如 清 單 7 所示。

如您所料,populator bean 將搜索整個 LDAP 目錄來查找經過驗證的用戶所 屬的組節點。然後讀取組節點的 ou 屬性的值,獲取用戶經過授權的業務角色。

這樣就完成了 populator bean 的配置。目前為止,我們在三個位置使用了 初始上下文:清單 5、清單 6 和 清單 7。接下來將了解如何配置初始上下文。

配置初始上下文

清單 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 在內部使用該類構造其他處理目錄操作(例如在整個目錄中 搜索)的類的對象。當配置初始上下文工廠時,必須指定以下內容:

將您的 LDAP 目錄和根目錄節點的網絡地址指定為構造函數的參數。在初始 上下文配置的節點將作為根節點。就是說所有後續操作(例如 search)都將在 根節點定義的子樹中執行。

將 DN 和密碼分別定義為 managerDn 和 managerPassword 屬性。在執行任 何搜索操作之前,Acegi 必須使用目錄服務器對 DN 和密碼進行身份驗證。

您已經了解了如何將用戶庫托管在 LDAP 目錄中,以及如何配置 Acegi 來使 用來自 LDAP 目錄的信息對用戶進行身份驗證。下一節將進一步介紹 Acegi 的 身份驗證處理過濾器,了解新配置的 bean 是如何管理身份驗證過程的。

身份驗證和授權

APF 配置完成後,將能夠與 LDAP 目錄進行通信來對用戶進行身份驗證。如 果您閱讀過第 1 部分,那麼對與目錄通信過程中 APF 執行的一些步驟不會感到 陌生,我在第 1 部分中向您展示了過濾器如何使用不同的服務進行用戶身份驗 證。圖 5 所示的序列表與您在 第 1 部分圖 3 看到的非常類似:

圖 5. APF 對一名 LDAP 用戶進行身份驗證

無論 APF 使用屬性文件進行內部的身份驗證還是與 LDAP 服務器進行通信, 步驟 1 到步驟 9 與第 1 部分是相同的。這裡簡單描述了前 9 個步驟,您可以 從步驟 10 開始繼續學習特定於 LDAP 的事件:

過濾器鏈前面的過濾器將請求、響應和過濾器鏈對象傳遞給 APF。

APF 使用取自請求對象的用戶名、密碼和其他信息創建一個身份驗證標記。

APF 將身份驗證標記傳遞給身份驗證管理器。

身份驗證管理器可能包含一個或多個身份驗證提供者。每個提供者恰好支持 一種身份驗證類型。管理器將檢查哪一種提供者支持從 APF 接收到的身份驗證 標記。

身份驗證管理器將身份驗證標記傳遞給適合該類型身份驗證的提供者。

身份驗證提供者從身份驗證標記中提取用戶名並將其傳遞到名為 user cache service 的服務。Acegi 緩存了已經進行過身份驗證的用戶。該用戶下次登錄時 ,Acegi 可以從緩存中加載他或她的詳細信息(比如用戶名、密碼和權限),而 不是從後端數據存儲中讀取數據。這種方法使得性能得到了改善。

user cache service 檢查用戶的詳細信息是否存在於緩存中。

user cache service 將用戶的詳細信息返回給身份驗證提供者。如果緩存不 包含用戶詳細信息,則返回 null。

身份驗證提供者檢查緩存服務返回的是用戶的詳細信息還是 null。

從這裡開始,身份驗證處理將特定於 LDAP。如果緩存返回 null,LDAP 身份 驗證提供者將把用戶名(在步驟 6 中提取的)和密碼傳遞給 清單 5 中配置的 authenticator bean。

authenticator 將使用在 清單 5 的 userDnPatterns 屬性中配置的 DN 模 式創建用戶 DN。通過從一個 DN 模式中創建一個 DN,然後將該 DN 和用戶密碼 (從用戶請求中獲得)發送到 LDAP 目錄,它將逐一嘗試所有可用的 DN 模式。 LDAP 目錄將檢查該 DN 是否存在以及密碼是否正確。如果其中任何一個 DN 模 式可行的話,用戶被綁定到 LDAP 目錄中,authenticator 將繼續執行步驟 15 。

如果任何一種 DN 模式都不能工作的話(這意味著在 DN 模式指定的任何位 置都不存在使用給定密碼的用戶),authenticator 根據 清單 6 配置的搜索查 詢在 LDAP 目錄中搜索用戶。如果 LDAP 目錄沒有找到用戶,那麼身份驗證以失 敗告終。

如果 LDAP 目錄查找到了用戶,它將用戶的 DN 返回到 authenticator。

authenticator 將用戶 DN 和密碼發送到 LDAP 目錄來檢查用戶密碼是否正 確。如果 LDAP 目錄發現用戶密碼是正確的,該用戶將被綁定到 LDAP 目錄。

authenticator 將用戶信息發送回 LDAP 身份驗證提供者。

LDAP 身份驗證提供者將控制權傳遞給 populator bean。

populator 搜索用戶所屬的組。

LDAP 目錄將用戶角色信息返回給 populator。

populator 將用戶角色信息返回給 LDAP 身份驗證提供者。

LDAP 身份驗證提供者將用戶的詳細信息(以及用戶業務角色信息)返回給 APF。用戶現在成功進行了身份驗證。

不論使用何種身份驗證方法,最後三個步驟是相同的(步驟21、21 和 23) 。

配置攔截器

您已經了解了 APF 對用戶進行身份驗證的步驟。接下來是查看成功進行身份 驗證的用戶是否被授權訪問所請求的資源。這項任務由 Acegi 的攔截過濾器 (Interceptor Filter,IF)完成。本節將向您展示如何配置 IF 來實現訪問控 制策略。

回想一下在 第 1 部分的清單 7 中配置 IF 的步驟。攔截過濾器在資源和角 色之間建立映射,就是說只有具備必要角色的用戶才能訪問給定資源。為了演示 制造業企業中不同部門的業務角色,清單 9 向現有的 IF 配置添加了另外的角 色:

清單 9. 配置攔截過濾器

<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">< BR>
<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 包含三個參數。其中第一個和第三個參數與第 1 部分中最 初配置的參數相同。這裡添加了第二個參數(名為 accessDecisionManager 的 bean)。

accessDecisionManager bean 負責指定授權決策。它使用清單 9 中第三個 參數提供的訪問控制定義來指定授權(或訪問控制)決策。第三個參數是 objectDefinitionSource。

配置訪問決策管理器

accessDecisionManager 決定是否允許一個用戶訪問某個資源。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 類的實例。 accessDecisionManager bean 只包含一個參數,即投票者(voter)列表。

在 Acegi 中,投票者確定是否允許某個用戶訪問特定的資源。當使用 accessDecisionManager 查詢時,投票者具有三個選項:允許訪問(access- granted)、拒絕訪問(access-denied),如果不確定的話則放棄投票 (abstain from voting)。

不同類型的訪問決策管理器解釋投票者決策的方法也有所不同。清單 10 所 示的 AffirmativeBased 訪問決策管理器實現了簡單的決策邏輯:如果任何投票 者強制執行肯定投票,將允許用戶訪問所請求的資源。

投票者邏輯

Acegi 提供了若干個投票者實現類型。accessDecisionManager 將經過驗證 的用戶的信息(包括用戶的業務角色信息)和 objectDefinitionSource 對象傳 遞給投票者。本文的示例使用了兩種類型的投票者,RoleVoter 和 AuthenticatedVoter,如清單 10 所示。現在看一下每種投票者的邏輯:

RoleVoter 只有在 objectDefinitionSource 對象的行中找到以 ROLE_ 前綴 開頭的角色時才進行投票。如果 RoleVoter 沒有找到這樣的行,將放棄投票; 如果在用戶業務角色中找到一個匹配的角色,它將投票給允許訪問;如果沒有找 到匹配的角色,則投票給拒絕訪問。在 清單 9 中,有兩個角色具有 ROLE_ 前 綴:ROLE_HEAD_OF_ENGINEERING 和 ROLE_HEAD_OF_MARKETING。

AuthenticatedVoter 只有在 objectDefinitionSource 對象中找到具有某個 預定義角色的行時才進行投票。在 清單 9 中,有這樣一行: IS_AUTHENTICATED_ANONYMOUSLY。匿名身份驗證意味著用戶不能夠進行身份驗證 。找到該行後,AuthenticatedVoter 將檢查一個匿名身份驗證的用戶是否可以 訪問某些不受保護的資源(即這些資源沒有包含在具備 ROLE_ 前綴的行中)。 如果 AuthenticatedVoter 發現所請求的資源是不受保護的並且 objectDefinitionSource 對象允許匿名身份驗證的用戶訪問不受保護的資源, 它將投票給允許訪問;否則就投票給拒絕訪問。

示例應用程序

本文提供了一個示例應用程序,它將演示您目前掌握的 LDAP 和 Acegi 概念 。LDAP-Acegi 應用程序將顯示一個索引頁面,該頁面將設計和銷售文檔呈現給 合適的經過身份驗證的用戶。正如您將看到的一樣,LDAP-Acegi 應用程序允許 用戶 alice 查看設計文檔,並允許用戶 bob 查看銷售文檔。它還允許特定用戶 同時查看設計和銷售文檔。所有這些內容都是在本文開頭配置 LDAP 目錄服務器 時設置的。

結束語

在本文中,您了解了如何將用戶和業務角色信息托管在 LDAP 目錄中。您還 詳細了解了配置 Acegi 的方法,從而與 LDAP 目錄交互實現訪問控制策略。在 本系列最後一期文章中,我將展示如何配置 Acegi 來保護對 Java 類的訪問。

本文配套源碼

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