#
2007 年 6 月 21 日
了解了 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 類的安全訪問。
您可以在本文的任何位置 下載樣例應用程序。參見 參考資料 下載 Acegi、Tomcat 和 ApacheDS,您需要使用它們運行樣例代碼和示例應用程序。
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 定義(參見 參考資料 中有關官方 RFC 文檔的鏈接),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、JXplorer 并了解更多有關兩者協作的信息。
在 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.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 的兩個屬性:
- 一個屬性名為
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 的詳細資料。)
您可以在 源代碼下載 部分查看 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.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
構造函數具有一個參數,使用 <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.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 的構造函數包括 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">
<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 類的訪問。
來自:http://www.cnblogs.com/amboyna/archive/2008/03/25/1122084.html
2007 年 5 月 08 日
這份共分三部分的系列文章介紹了 Acegi 安全系統(Acegi Security System),它是用于 Java™ 企業應用程序的強大的開源安全框架。在第一篇文章中,Bilal Siddiqui 顧問將向您介紹 Acegi 的架構和組件,并展示如何使用它來保護一個簡單的 Java 企業應用程序。
Acegi Security System 是一種功能強大并易于使用的替代性方案,使您不必再為 Java 企業應用程序編寫大量的安全代碼。雖然它專門針對使用 Spring 框架編寫的應用程序,但是任何類型的 Java 應用程序都沒有理由不去使用 Acegi。這份共分三部分的系列文章詳細介紹了 Acegi,并展示了如何使用它保護簡單的企業應用程序以及更復雜的應用程序。
本系列首先介紹企業應用程序中常見的安全問題,并說明 Acegi 如何解決這些問題。您將了解 Acegi 的架構模型及其安全過濾器,后者包含了在保護應用程序中將用到的大多數功能。您還將了解到各個過濾器如何單獨進行工作,如何將它們組合起來,以及過濾器如何在一個企業安全實現中將各種功能從頭到尾地鏈接起來。本文最后通過一個樣例應用程序演示了基于 URL 安全系統的 Acegi 實現。本系列后續兩篇文章將探究 Acegi 的一些更高級的應用,包括如何設計和托管訪問控制策略,然后如何去配置 Acegi 以使用這些策略。
您必須 下載 Acegi,這樣才能編譯本文的示例代碼并運行本文的樣例應用程序。還必須有作為工作站的一部分運行的 Tomcat 服務器。
企業應用程序安全性
由于企業內容管理(ECM)應用程序管理存儲在不同類型數據源(如文件系統、關系數據庫和目錄服務)中的企業內容的編寫和處理,ECM 安全性要求對這些數據源的訪問進行控制。比如,一個 ECM 應用程序可能會控制被授權讀取、編輯或刪除數據的對象,而這些數據和制造業企業的設計、市場營銷、生產以及質量控制有關。
在一個 ECM 安全場景中,比較常見的就是通過對企業資源定位符(或網絡地址)應用安全性,從而實現訪問控制。這種簡單的安全模型被稱為統一資源定位符 或 URL 安全性。正如我在本文后面(以及本系列后續文章)所演示的一樣,Acegi 為實現 URL 安全性提供了全面的特性。
然而,在很多企業場景中,URL 安全性還遠遠不夠。比如,假設一個 PDF 文檔包含某個制造業公司生產的特殊產品的數據。文檔的一部分包含了將由該公司的設計部門編輯和更新的設計數據。另一部分包含了生產經理將使用的生產數據。對于諸如此類的場景,需要實現更加細粒度的安全性,對文檔的不同部分應用不同的訪問權限。
本文介紹了 Acegi 為實現 URL 安全性而提供的各種功能。本系列的下一篇文章將演示此框架的基于方法的安全性,它提供了對企業數據訪問的更細粒度的控制。
Acegi Security System
Acegi Security System 使用安全過濾器來提供企業應用程序的身份驗證和授權服務。該框架提供了不同類型的過濾器,可以根據應用程序的需求進行配置。您將在本文后面了解到 安全過濾器的不同類型;現在,只需注意可以為如下任務配置 Acegi 安全過濾器:
- 在訪問一個安全資源之前提示用戶登錄。
- 通過檢查安全標記(如密碼),對用戶進行身份驗證。
- 檢查經過身份驗證的用戶是否具有訪問某個安全資源的特權。
- 將成功進行身份驗證和授權的用戶重定向到所請求的安全資源。
- 對不具備訪問安全資源特權的用戶顯示 Access Denied 頁面。
- 在服務器上記錄成功進行身份驗證的用戶,并在用戶的客戶機上設置安全 cookie。使用該 cookie 執行下一次身份驗證,而無需要求用戶登錄。
- 將身份驗證信息存儲在服務器端的會話對象中,從而安全地進行對資源的后續請求。
- 在服務器端對象中構建并保存安全信息的緩存,從而優化性能。
- 當用戶退出時,刪除為用戶安全會話而保存的服務器端對象。
- 與大量后端數據存儲服務(如目錄服務或關系數據庫)進行通信,這些服務用于存儲用戶的安全信息和 ECM 的訪問控制策略。
正如這個列表顯示的那樣,Acegi 的安全過濾器允許您執行保護企業應用程序所需的幾乎任何事情。
架構和組件
對 Acegi 了解越多,使用起來就越簡單。這一節介紹 Acegi 的組件;接下來您將了解該框架如何使用反轉控制(IOC)和 XML 配置文件來組合組件并表示它們的依賴關系。
四大組件
Acegi Security System 由四種主要類型的組件組成:過濾器、管理器、提供者和處理程序。
- 過濾器
- 這種最高級的組件提供了常見的安全服務,如身份驗證、會話處理以及注銷。我將在 本文后面的部分 深入討論過濾器。
- 管理器
- 過濾器僅是安全相關功能的高級抽象:實際上要使用管理器和提供者實現身份驗證處理和注銷服務。管理器管理由不同提供者提供的較低級的安全服務。
- 提供者
- 有大量的提供者可用于和不同類型的數據存儲服務通信,例如目錄服務、關系數據庫或簡單的內存中的對象。這意味著您可以將用戶庫和訪問控制協議存儲在任何一種這樣的數據存儲服務中,并且 Acegi 的管理器將在運行時選擇合適的提供者。
- 處理程序
- 有時任務可能會被分解為多個步驟,其中每個步驟由一個特定的處理程序執行。比方說,Acegi 的 注銷過濾器 使用兩個處理程序來退出一個 HTTP 客戶機。其中一個處理程序使用戶的 HTTP 會話無效,而另一個處理程序則刪除用戶的 cookie。當根據應用程序需求配置 Acegi 時,多個處理程序能夠提供很好的靈活性。您可以使用自己選擇的處理程序來執行保護應用程序所需的步驟。
反轉控制
Acegi 的組件通過彼此之間的依賴來對企業應用程序進行保護。比如,一個身份驗證處理過濾器需要一個身份驗證管理器選擇一個合適的身份驗證提供者。這就是說您必須能夠表示和管理 Acegi 組件的依賴關系。
IOC 實現通常用于管理 Java 組件之間的依賴關系。IOC 提供了兩個重要的特性:
- 它提供了一種語法,表示應用程序所需的組件以及這些組件如何相互依賴。
- 它保證了所需的組件在運行時是可用的。
XML 配置文件
Acegi 使用 Spring 框架(請參見 參考資料)附帶的流行開源 IOC 實現來管理其組件。Spring 需要您編寫一個 XML 配置文件來表示組件的依賴關系,如清單 1 所示:
清單 1. Spring 配置文件的結構
<beans>
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value> value here </value>
</property>
</bean>
<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFitler">
<property name="authenticationManager" ref="authManager"/>
<!-- Other properties -->
</bean>
<bean id="authManager"
class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<!-- List of providers here -->
</property>
</bean>
<!-- Other bean tags -->
</beans>
|
如您所見,Acegi 使用的 Spring XML 配置文件包含一個 <beans>
標記,它封裝了一些其他的 <bean>
標記。所有的 Acegi 組件(即過濾器、管理器、提供者等)實際上都是 JavaBean。XML 配置文件中的每個 <bean>
標記都代表一個 Acegi 組件。
進一步解釋 XML 配置文件
首先將注意到的是每個 <bean>
標記都包含一個 class 屬性,這個屬性標識組件所使用的類。<bean>
標記還具有一個 id 屬性,它標識作為 Acegi 組件工作的實例(Java 對象)。
比方說,清單 1 的第一個 <bean>
標記標識了名為 filterChainProxy
的組件實例,它是名為 org.acegisecurity.util.FilterChainProxy
的類的實例。
使用 <bean>
標記的子標記來表示 bean 的依賴關系。比如,注意第一個 <bean>
標記的 <property>
子標記。<property>
子標記定義了 <bean>
標記依賴的其他 bean 或值。
所以在 清單 1 中,第一個 <bean>
標記的 <property>
子標記具有一個 name 屬性和一個 <value>
子標記,分別定義了這個 bean 依賴的屬性的名稱和值。
同樣,清單 1 中的第二個和第三個 <bean>
標記定義了一個過濾器 bean 依賴于一個管理器 bean。第二個 <bean>
標記表示過濾器 bean,而第三個 <bean>
標記表示管理器 bean。
過濾器的 <bean>
標記包含一個 <property>
子標記,該子標記具有兩個屬性:name
和 ref
。name
屬性定義了過濾器 bean 的屬性,而 ref
屬性引用了管理器 bean 的實例(名稱)。
下一節將展示如何在 XML 配置文件中配置 Acegi 過濾器。在本文后面的內容中,您將在一個樣例 Acegi 應用程序中使用過濾器。
安全過濾器
正如我前面提到的一樣,Acegi 使用安全過濾器為企業應用程序提供身份驗證和授權服務。您可以根據應用程序的需要使用和配置不同類型的過濾器。這一節將介紹五種最重要的 Acegi 安全過濾器。
Session Integration Filter
Acegi 的 Session Integration Filter(SIF)通常是您將要配置的第一個過濾器。SIF 創建了一個安全上下文對象,這是一個與安全相關的信息的占位符。其他 Acegi 過濾器將安全信息保存在安全上下文中,也會使用安全上下文中可用的安全信息。
SIF 創建安全上下文并調用過濾器鏈中的其他過濾器。然后其他過濾器檢索安全上下文并對其進行更改。比如,Authentication Processing Filter(我將稍后進行介紹)將用戶信息(如用戶名、密碼和電子郵件地址)存儲在安全上下文中。
當所有的處理程序完成處理后,SIF 檢查安全上下文是否更新。如果任何一個過濾器對安全上下文做出了更改,SIF 將把更改保存到服務器端的會話對象中。如果安全上下文中沒有發現任何更改,那么 SIF 將刪除它。
在 XML 配置文件中對 SIF 進行了配置,如清單 2 所示:
清單 2. 配置 SIF
<bean id="httpSessionContextIntegrationFilter"
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/>
|
Authentication Processing Filter
Acegi 使用 Authentication Processing Filter(APF)進行身份驗證。APF 使用一個身份驗證(或登錄)表單,用戶在其中輸入用戶名和密碼,并觸發身份驗證。
APF 執行所有的后端身份驗證處理任務,比如從客戶機請求中提取用戶名和密碼,從后端用戶庫中讀取用戶參數,以及使用這些信息對用戶進行身份驗證。
在配置 APF 時,您必須提供如下參數:
- Authentication manager 指定了用來管理身份驗證提供者的身份驗證管理器。
- Filter processes URL 指定了客戶在登錄窗口中按下 Sign In 按鈕時要訪問的 URL。收到這個 URL 的請求后,Acegi 立即調用 APF。
- Default target URL 指定了成功進行身份驗證和授權后呈現給用戶的頁面。
- Authentication failure URL 指定了身份驗證失敗情況下用戶看到的頁面。
APF 從用戶的請求對象中得到用戶名、密碼和其他信息。它將這些信息傳送給身份驗證管理器。身份驗證管理器使用適當的提供者從后端用戶庫中讀取詳細的用戶信息(如用戶名、密碼、電子郵件地址和用戶訪問權利或特權),對用戶進行身份驗證,并將信息存儲在一個 Authentication
對象中。
最后,APF 將 Authentication
對象保存在 SIF 之前創建的安全上下文中。存儲在安全上下文中的 Authentication
對象將用于做出授權決策。
APF 的配置如清單 3 所示:
清單 3. 配置 APF
<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager"
ref="authenticationManager" />
<property name="filterProcessesUrl"
value="/j_acegi_security_check" />
<property name="defaultTargetUrl"
value="/protected/protected1.jsp" />
<property name="authenticationFailureUrl"
value="/login.jsp?login_error=1" />
</bean>
|
可以從這段代碼中看到,APF 依賴于上面討論的這四個參數。每個參數都是作為清單 3 所示的 <property>
標記配置的。
Logout Processing Filter
Acegi 使用一個 Logout Processing Filer(LPF)管理注銷處理。當客戶機發來注銷請求時,將使用 LPF 進行處理。它標識了來自由客戶機所調用 URL 的注銷請求。
LPF 的配置如清單 4 所示:
清單 4. 配置 LPF
<bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
<constructor-arg value="/logoutSuccess.jsp"/>
<constructor-arg>
<list>
<bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
</list>
</constructor-arg>
</bean>
|
可以看到 LPF 在其構造方法中包含兩個參數:注銷成功 URL(/logoutSuccess.jsp
)和處理程序列表。注銷成功 URL 用來在注銷過程完成后重定向客戶機。處理程序執行實際的注銷過程;我在這里只配置了一個處理程序,因為只需一個處理程序就可以使 HTTP 會話變為無效。我將在本系列下一篇文章中討論更多的處理程序。
Exception Translation Filter
Exception Translation Filter(ETF)處理身份驗證和授權過程中的異常情況,比如授權失敗。在這些異常情況中,ETF 將決定如何進行操作。
比如,如果一個沒有進行身份驗證的用戶試圖訪問受保護的資源,ETF 將顯示一個登錄頁面要求用戶進行身份驗證。類似地,在授權失敗的情況下,可以配置 ETF 來呈現一個 Access Denied 頁面。
ETF 的配置如清單 5 所示:
清單 5. 配置 ETF
<bean id="exceptionTranslationFilter"
class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/login.jsp" />
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
<property name="errorPage" value="/accessDenied.jsp" />
</bean>
</property>
</bean>
|
正如清單 5 所示,ETF 包含兩個參數,名為 authenticationEntryPoint
和 accessDeniedHandler
。authenticationEntryPoint
屬性指定登錄頁面,而 accessDeniedHandler
指定 Access Denied 頁面。
攔截過濾器
Acegi 的攔截過濾器 用于做出授權決策。您需要在 APF 成功執行身份驗證后對攔截過濾器進行配置,以使其發揮作用。攔截器使用應用程序的訪問控制策略來做出授權決定。
本系列的下一篇文章將展示如何設計訪問控制策略,如何將它們托管在目錄服務中,以及如何配置 Acegi 以讀取您的訪問控制策略。但是,目前我將繼續向您展示如何使用 Acegi 配置一個簡單的訪問控制策略。在本文后面的部分,您將看到使用簡單的訪問控制策略構建一個樣例應用程序。
配置簡單的訪問控制策略可分為兩個步驟:
- 編寫訪問控制策略。
- 根據策略配置 Acegi 的攔截過濾器。
步驟 1. 編寫簡單的訪問控制策略
首先看一下 清單 6,它展示了如何定義一個用戶及其用戶角色:
清單 6. 為用戶定義簡單的訪問控制策略
alice=123,ROLE_HEAD_OF_ENGINEERING
|
清單 6 所示的訪問控制策略定義了用戶名 alice
,它的密碼是 123
,角色是 ROLE_HEAD_OF_ENGINEERING
。(下一節將說明如何在文件中定義任意數量的用戶及其用戶角色,然后配置攔截過濾器以使用這些文件。)
步驟 2. 配置 Acegi 的攔截過濾器
攔截過濾器使用三個組件來做出授權決策,我在清單 7 中對其進行了配置:
清單 7. 配置攔截過濾器
<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/**=ROLE_HEAD_OF_ENGINEERING
/**=IS_AUTHENTICATED_ANONYMOUSLY
</value>
</property>
<!-- More properties of the interceptor filter -->
</bean>
|
如清單 7 所示,配置所需的三個組件是 authenticationManager
、accessDecisionManager
、objectDefinitionSource
:
authenticationManager
組件與我在介紹 Authentication Processing Filter 時討論過的身份驗證管理器相同。攔截過濾器可以在授權的過程中使用 authenticationManager
重新對客戶機進行身份驗證。
accessDecisionManager
組件管理授權過程,這部分內容將在本系列的下篇文章中詳細討論。
objectDefinitionSource
組件包含對應于將要發生的授權的訪問控制定義。例如,清單 7 中的 objectDefinitionSource
屬性值包含兩個 URL(/protected/*
和 /*
)。其值定義了這些 URL 的角色。/protected/*
URL 的角色是 ROLE_HEAD_OF_ENGINEERING
。您可以根據應用程序的需要定義任何角色。
回想一下 清單 6,您為用戶名 alice
定義了 ROLE_HEAD_OF_ENGINEERING
。這就是說 alice
將能夠訪問 /protected/*
URL。
過濾器工作原理
正如您已經了解到的一樣,Acegi 的組件彼此依賴,從而對您的應用程序進行保護。在本文后面的部分,您將看到如何對 Acegi 進行配置,從而按照特定的順序應用安全過濾器,因此需要創建過濾器鏈。出于這個目的,Acegi 保存了一個過濾器鏈對象,它封裝了為保護應用程序而配置了的所有過濾器。圖 1 展示了 Acegi 過濾器鏈的生命周期,該周期從客戶機向您的應用程序發送 HTTP 請求開始。(圖 1 顯示了服務于瀏覽器客戶機的容器。)
圖 1. 托管 Acegi 過濾器鏈以安全地為瀏覽器客戶機服務的容器
下面的步驟描述了過濾器鏈的生命周期:
- 瀏覽器客戶機向您的應用程序發送 HTTP 請求。
- 容器接收到 HTTP 請求并創建一個請求對象,該對象將封裝 HTTP 請求中包含的信息。容器還創建一個各種過濾器都可處理的響應對象,從而為發出請求的客戶機準備好 HTTP 響應。容器然后調用 Acegi 的過濾器鏈代理,這是一個代理過濾器。該代理知道應用的過濾器的實際順序。當容器調用代理時,它將向代理發送請求、響應以及過濾器鏈對象。
- 代理過濾器調用過濾器鏈中第一個過濾器,向其發送請求、響應和過濾器鏈對象。
- 鏈中的過濾器逐個執行其處理。一個過濾器可以通過調用過濾器鏈中下一個過濾器隨時終止自身處理。有的過濾器甚至根本不執行任何處理(比如,如果 APF 發現一個到來的請求沒有要求身份驗證,它可能會立即終止其處理)。
- 當身份驗證過濾器完成其處理時,這些過濾器將把請求和響應對象發送到應用程序中配置的攔截過濾器。
- 攔截器決定是否對發出請求的客戶機進行授權,使它訪問所請求的資源。
- 攔截器將控制權傳輸給應用程序(比如,成功進行了身份驗證和授權的客戶機請求的 JSP 頁面)。
- 應用程序改寫響應對象的內容。
- 響應對象已經準備好了,容器將響應對象轉換為 HTTP 響應,并將響應發送到發出請求的客戶機。
為幫助您進一步理解 Acegi 過濾器,我將詳細探討其中兩個過濾器的操作:Session Integration Filter 和 Authentication Processing Filter。
SIF 如何創建一個安全上下文
圖 2 展示了 SIF 創建安全上下文所涉及到的步驟:
圖 2. SIF 創建安全上下文
現在詳細地考慮下面這些步驟:
- Acegi 的過濾器鏈代理調用 SIF 并向其發送請求、響應和過濾器鏈對象。注意:通常將 SIF 配置為過濾器鏈中第一個過濾器。
- SIF 檢查它是否已經對這個 Web 請求進行過處理。如果是的話,它將不再進一步進行處理,并將控制權傳輸給過濾器鏈中的下一個過濾器(參見下面的第 4 個步驟)。如果 SIF 發現這是第一次對這個 Web 請求調用 SIF,它將設置一個標記,將在下一次使用該標記,以表示曾經調用過 SIF。
- SIF 將檢查是否存在一個會話對象,以及它是否包含安全上下文。它從會話對象中檢索安全上下文,并將其放置在名為 security context holder 的臨時占位符中。如果不存在會話對象,SIF 將創建一個新的安全上下文,并將它放到 security context holder 中。注意:security context holder 位于應用程序的范圍內,所以可以被其他的安全過濾器訪問。
- SIF 調用過濾器鏈中的下一個過濾器。
- 其他過濾器可以編輯安全上下文。
- SIF 在過濾器鏈完成處理后接收控制權。
- SIF 檢查其他的過濾器是否在其處理過程中更改了安全上下文(比如,APF 可能將用戶詳細信息存儲在安全上下文中)。如果是的話,它將更新會話對象中的安全上下文。就是說在過濾器鏈處理過程中,對安全上下文的任何更改現在都保存在會話對象中。
APF 如何對用戶進行身份驗證
圖 3 展示了 APF 對用戶進行身份驗證所涉及到的步驟:
圖 3. APF 對用戶進行身份驗證
現在仔細考慮以下這些步驟:
- 過濾器鏈中前面的過濾器向 APF 發送請求、響應和過濾鏈對象。
- APF 使用從請求對象中獲得的用戶名、密碼以及其他信息創建身份驗證標記。
- APF 將身份驗證標記傳遞給身份驗證管理器。
- 身份驗證管理器可能包含一個或更多身份驗證提供者。每個提供者恰好支持一種類型的身份驗證。管理器檢查哪一種提供者支持它從 APF 收到的身份驗證標記。
- 身份驗證管理器將身份驗證標記發送到適合進行身份驗證的提供者。
- 身份驗證提供者支持從身份驗證標記中提取用戶名,并將它發送給名為 user cache service 的服務。Acegi 緩存了已經進行過身份驗證的用戶。該用戶下次登錄時,Acegi 可以從緩存中加載他或她的詳細信息(比如用戶名、密碼和權限),而不是從后端數據存儲中讀取數據。這種方法使得性能得到了改善。
- user cache service 檢查用戶的詳細信息是否存在于緩存中。
- user cache service 將用戶的詳細信息返回給身份驗證提供者。如果緩存不包含用戶詳細信息,則返回 null。
- 身份驗證提供者檢查緩存服務返回的是用戶的詳細信息還是 null。
- 如果緩存返回 null,身份驗證提供者將用戶名(在步驟 6 中提取)發送給另一個名為 user details service 的服務。
- user details service 與包含用戶詳細信息的后端數據存儲通信(如目錄服務)。
- user details service 返回用戶的詳細信息,或者,如果找不到用戶詳細信息則拋出身份驗證異常。
- 如果 user cache service 或者 user details service 返回有效的用戶詳細信息,身份驗證提供者將使用 user cache service 或 user details service 返回的密碼來匹配用戶提供的安全標記(如密碼)。如果找到一個匹配,身份驗證提供者將用戶的詳細信息返回給身份驗證管理器。否則的話,則拋出一個身份驗證異常。
- 身份驗證管理器將用戶的詳細信息返回給 APF。這樣用戶就成功地進行了身份驗證。
- APF 將用戶詳細信息保存在 圖 2 所示由步驟 3 創建的安全上下文中。
- APF 將控制權傳輸給過濾器鏈中的下一個過濾器。
一個簡單的 Acegi 應用程序
在本文中,您已經了解了很多關于 Acegi 的知識,所以現在看一下利用您目前學到的知識能做些什么,從而結束本文。對于這個簡單的演示,我設計了一個樣例應用程序(參見 下載),并對 Acegi 進行了配置以保護它的一些資源。
樣例應用程序包含 5 個 JSP 頁面:index.jsp、protected1.jsp、protected2.jsp、login.jsp 和 accessDenied.jsp。
index.jsp 是應用程序的歡迎頁面。它向用戶顯示了三個超鏈接,如圖 4 所示:
圖 4. 樣例應用程序的歡迎頁面:
圖 4 所示的鏈接中,其中兩個鏈接指向了被保護的資源(protected1.jsp 和 protected2.jsp),第三個鏈接指向登錄頁面(login.jsp)。只有在 Acegi 發現用戶沒有被授權訪問受保護的資源時,才會顯示 accessDenied.jsp 頁面。
如果用戶試圖訪問任何受保護的頁面,樣例應用程序將顯示登錄頁面。當用戶使用登錄頁面進入后,應用程序將自動重定向到被請求的受保護資源。
用戶可以通過單擊歡迎頁面中的第三個鏈接直接請求登錄頁面。這種情況下,應用程序顯示用戶可以進入系統的登錄頁面。進入系統以后,應用程序將用戶重定向到 protected1.jsp,它是用戶進入系統而沒有請求特定的受保護資源時顯示的默認資源。
配置樣例應用程序
為本文下載的源代碼包含一個名為 acegi-config.xml 的 XML 配置文件,它包含 Acegi 過濾器的配置。根據 安全過濾器的討論 中的示例,您應該很熟悉這些配置。
我還為樣例應用程序編寫了一個 web.xml
文件,如清單 8 所示:
清單 8. 樣例應用程序的 web.xml 文件
<web-app>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/acegi-config.xml</param-value>
</context-param>
<filter>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<filter-class>
org.acegisecurity.util.FilterToBeanProxy
</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>
org.acegisecurity.util.FilterChainProxy
</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
</web-app>
|
web.xml 文件配置如下:
acegi-config.xml
文件的 URL 位于 <context-param>
標記中。
- Acegi 過濾器鏈代理類的名稱位于
<filter>
標記中。
- URL 到 Acegi 過濾器鏈代理的映射在
<filter-mapping>
標記中。注意:您可以簡單地將應用程序的所有 URL(/*
)映射到 Acegi 過濾器鏈代理。Acegi 將對映射到 Acegi 過濾器鏈代理上的所有 URL 應用安全性。
- 應用程序上下文加載程序位于
<listener>
標記中,它將加載 Spring 的 IOC 框架。
部署并運行應用程序
部署并運行樣例應用程序非常的簡單。只需要完成兩件事情:
- 將 acegisample.war 文件從本教程下載的源代碼中復制到安裝 Tomcat 的
webapps
目錄中。
- 從 Acegi Security System 主頁 下載并解壓縮 acegi-security-1.0.3.zip。您將找到一個名為 acegi-security-sample-tutorial.war 的樣例應用程序。解壓縮 war 文件并提取其 WEB-INF/lib 文件夾中所有的 jar 文件。將所有的 JAR 文件從 WEB-INF/lib 文件夾中復制到 theacegisample.war 應用程序的 WEB-INF/lib 文件夾。
現在,您已經為運行樣例應用程序做好準備了。啟動 Tomcat 并將瀏覽器指向 http://localhost:8080/acegisample/
。
您將看到 圖 4 所示的歡迎頁面,但是此時顯示的頁面是真實的。請繼續運行程序,并查看在嘗試訪問歡迎頁面顯示的不同鏈接時會發生什么狀況。
結束語
在使用 Acegi 保護 Java 應用程序 系列的第一篇文章中,您了解了 Acegi 安全系統的特性、架構和組件,學習了大量有關 Acegi 安全過濾器的知識,這些過濾器被集成到 Acegi 的安全框架中。您還學習了如何使用 XML 配置文件配置組件依賴關系,并查看了 Acegi 的安全過濾器在樣例程序中工作的情形,該應用程序可以實現基于 URL 的安全性。
本文所述的安全技術非常的簡單,所以 Acegi 使用這些技術實現安全性。本系列的下一文章將開始介紹 Acegi 的一些較為高級的應用,首先是編寫訪問控制協議并將其存儲到目錄服務中。您還將了解到如何配置 Acegi,使它與目錄服務交互,從而實現您的訪問控制策略。
下載
描述 |
名字 |
大小 |
下載方法 |
本文源代碼 |
j-acegi1.zip |
10KB |
HTTP |
參考資料
學習
獲得產品和技術
討論
關于作者
 |
|
 |
Bilal Siddiqui 是一名電子工程師、XML 顧問,他還是 WaxSys(主要從事電子商務簡化)的創建者之一。自從 1995 年畢業于拉合爾工程技術大學(University of Engineering and Technology,Lahore)電子工程專業以后,他就開始為工業控制系統設計各種軟件解決方案。稍后,他致力于 XML 方面并使用他在 C++ 編程中取得的經驗來構建基于 Web 和 WAP 的 XML 處理工具、服務器端解析方案和服務應用程序。Bilal 是一名技術推廣者,并且是一名多產的技術作家。
|
來自:http://www.cnblogs.com/amboyna/archive/2008/03/25/1122079.html
最近項目使用Acegi作為安全框架的實現,效果不錯,就寫了這篇文章作為總結.
對于任何一個完整的應用系統,完善的認證和授權機制是必不可少的。在基于SpringFramework的WEB應用中,
我們可以使用Acegi作為安全架構的實現。本文將介紹如何在基于Spring構架的Web應用中使用Acegi,并且詳細介
紹如何配置和擴展Acegi框架以適應實際需要。
文章和代碼下載:
http://m.tkk7.com/Files/youlq/Acegi.zip
注意:許多朋友在部署上遇到一些麻煩,所以我將可以部署的完整的war文件傳上來,注意:java代碼在acegi-sample.war\WEB-INF 目錄下,例子需要Mysql,建庫腳本在acegi-sample.war\db目錄下。
acegi-sample.part1.rar
acegi-sample.part2.rar
acegi-sample.part3.rar
acegi-sample.part4.rar
附注:
有些朋友詢問我如何部署文中的例子,在此再次說明一下(文章中已經有提到):
Mysql的建表腳本在db目錄下
為了減小體積,已經將WEB-INF\lib下的依賴包刪除,請自行下載以下包,并拷貝至WEB-INF\lib下:
spring-1.2.4.jar
acegi-security-0.8.3.jar
aopalliance-1.0.jar
c3p0-0.9.0.jar
commons-logging-1.0.4.jar
ehcache-1.1.jar
log4j-1.2.8.jar
mysql-connector-java-3.1.10-bin.jar
oro-2.0.8.jar
提示:
acegi-security-0.8.3.jar
aopalliance-1.0.jar
c3p0-0.9.0.jar
commons-logging-1.0.4.jar
ehcache-1.1.jar
log4j-1.2.8.jar
oro-2.0.8.jar
可以在acegi-security-0.8.3.zip所帶的acegi-security-sample-contacts-filter.war中找到。
spring-1.2.4.jar
mysql-connector-java-3.1.10-bin.jar
要分別到springframework和mysql網站下載。
來自:http://m.tkk7.com/youlq/archive/2005/12/06/22678.html
1 Acegi官方發布版的下載和安裝
開發者可以通過http://sourceforge.net/projects/acegisecurity或http://acegisecurity.org/下載到Acegi官方發布版,比如acegi-security-1.x.zip。圖4-4展示了SF中Acegi項目的首頁,它提供了下載Acegi(Spring Security)的入口。

圖4-4 http://sourceforge.net/projects/acegisecurity首頁
在單擊圖4-4中的下載(“Download Acegi Security System for Spring”)超鏈接后,開發者進而能夠下載到最新的Acegi官方發布包。此時,建議開發者同時將acegi-security-1.x.zip、acegi-security-1.x-src.zip下載到本地,前者包含了Jar存檔和若干Acegi使能應用,而后者僅僅包含了Acegi項目的源代碼。
在下載到Acegi官方發布版后,通過解壓acegi-security-1.x.zip,開發者能夠瀏覽到如圖4-5所示的類似內容。

圖4-5 acegi-security-1.x.zip包含的內容
通常,大部分Acegi安全性項目僅僅需要使用到acegi-security-1.x.jar存檔,這是Acegi的核心包,任何Acegi使能項目都需要使用到它。如果項目打算采用Java SE 5引入的Annotation注釋支持,則還需要將acegi-security-tiger-1.x.jar添加到WEB-INF/lib中。如果開發者在使用Acegi提供的容器適配器支持,則還需要將acegi-security-catalina-1.x.jar(針對Tomcat)、acegi-security-jboss-1.x.jar(針對JBoss)、acegi-security-jetty-1.x.jar(針對Jetty)、acegi-security-resin-1.x.jar(針對Resin)等Jar存檔復制到相應的位置,至于這些Jar包的具體使用,本書將在第10章詳細闡述。
另外,acegi-security-sample-contacts-filter.war、acegi-security-sample-tutorial.war是兩個直接可部署到Java EE容器(Tomcat容器)中的Web應用。
2 Subversion中的Acegi源碼下載和安裝
如今,Acegi基代碼采用Subversion管理。開發者通過如圖4-6所示的Web頁面能夠獲得Subversion下載地址(http://sourceforge.net/svn/?group_id=104215)。

圖4-6 獲得下載Acegi基代碼地址的Web頁面
事實上,Acegi(Spring Security)基代碼本身就是一個Eclipse Java項目,而且它的構建、管理工作采用了Maven 1.x/2.x(http://maven.apache.org/)。開發者可以借助Eclipse Subversive插件從Subversion存儲源獲得Acegi的最新基代碼。圖4-7展示了Subversive內置的SVN Repository Exploring。

圖4-7 Subversive插件使用截圖
一旦在下載完成Acegi(Spring Security)基代碼后,開發者將能夠持續監控到Acegi項目的最新情況,比如獲得Acegi持續更新的基代碼、Acegi官方文檔,圖4-8展示了相應的使用截圖。

圖4-8 持續更新Acegi基代碼
3 有關Acegi的權威去處
其一,開發者可以去訂閱acegisecurity-developer@lists.sourceforge.net郵件列表,圖4-9展示了訂閱這一郵件列表的入口。Acegi開發團隊積極參與到這一郵件列表中,因此開發者從這一郵件列表能夠獲得Acegi的最新進展。

圖4-9 訂閱Acegi開發者郵件列表
其二,Acegi官方論壇(http://forum.springframework.org/),圖4-10展示了論壇截圖。

圖4-10 Acegi官方論壇
開發者可以通過許多渠道獲得一手的Acegi知識、開發和部署經驗。
5 小結
本章圍繞Acegi(Spring Security)的認證策略進行了闡述,比如基于過濾器的設計、與認證源解耦、AcegiSecurityException異常體系等。另外,我們還針對Acegi發布版和基代碼的下載進行了簡要介紹。
下章將深入到Acegi支持的各種認證機制中。
【參考及推薦資料】
l http://acegisecurity.org/
l http://sourceforge.net/projects/acegisecurity
l http://www.polarion.org/index.php?page=overview&project=subversive
來自:http://book.csdn.net/bookfiles/310/10031012826.shtml
如何實施內容管理解決方案呢?讓我們借鑒一下如下做法。
IBM內容管理第8版(CM V8)提供了一套可靠的、易升級的、強勁的企業內容管理體系架構,它也同時提供了強勁的、安全的和高擴展能力的服務,使得企業用戶能非常容易地訪問電子商務的內容。這種體系架構能夠通過具有強勁、靈活的數據模型的單一存儲庫來支持不同的、異構的內容管理技術,包括文檔管理、數字媒體資產管理、網站內容管理等,這種數據模型使得我們能非常容易地在不同系統之間實現內容共享,從而提高了業務處理流程效率。
IBM CM V8也提供了各種特性來滿足對內容整個生命周期的管理,包括捕獲、存儲、組織、流轉、歸檔、跟蹤和銷毀。
IBM內容管理解決方案平臺基于用戶對于內容進行管理的需求而開發,并且IBM內容管理產品第8版本有了更多的新功能。
高擴展性的體系架構
CM服務器由一個索引服務器(Library Server)、一個或多個對象服務器(Object Server或稱Resource Manager)和一個或多個客戶程序構成,通過提供具有專利技術的三角形架構的體系結構,使得系統能依據需求隨意擴展。資源管理服務器能分布在網絡上的任何地方,并且通過統一的索引服務器而實現集中管理。它以堅實的技術實力為基礎,融合了基于SQL標準的關系數據庫。系統具有很強的擴容性,可以由單機系統擴容到SMP并行多處理器系統和SP超級并行處理計算機系統都可以適用。通過實現分布式基于對象的兩階段提交技術,確保索引數據和媒體數據的參照完整性,從而不會出現孤島式的內容對象。
靈活、強勁的數據模型
IBM CM V8提供了靈活、強勁的數據模型,通過CM V8的數據模型,能夠非常容易地實現任何復雜和多變的元數據標準。簡單來說,任何用XML格式描述的元數據信息,都可以在CM中得到快速實現,并且靈活地進行改變,從而充分和容易地通過客戶化來實現各種元數據標準,而不需要任何系統設計和編程工作,且實現系統性能的最優化。
高效的查詢訪問能力
CM提供了多種查詢方法,包括參數檢索、全文檢索以及面向中文的中文智能檢索,并且CM能支持用戶同時和分開使用這幾種檢索方法。
通過參數檢索,用戶能快速訪問所有元數據信息,象客戶名稱、客戶號等。全文檢索能對已建立了文本索引的內容進行自由文本檢索,從而快速查找到相關的文檔;而中文智能檢索能力將基于中文語義進行檢索,支持包括近義詞、上下位概念、同一名稱等智能檢索。
企業范圍的內容整合
企業都有許多的信息孤島在管理著數據和數字內容,這些解決方案都有它們自己的方式來訪問信息,包括語法、語義和接口都不一樣。定制的應用開發需要從這些分離的數據源和專門的過濾方法來收集合訪問所有的信息。CM通過提供信息整合的體系架構來實現單一入口的針對內容存儲庫的訪問接口。
分布式存儲和多級存儲管理
CM的資源管理器是對象存儲庫,一個索引服務器能支持多個本地和遠程的資源管理器,用戶通過索引服務器來對對象進行存儲和訪問,資源管理器基于WebSphere的最新技術建立,通過標準的HTTP、FTP和FILE協議來進行通訊,支持基于Internet的應用。資源管理器和索引服務器合作來確保數據完整性和一致性,資源管理器通過數據庫來記錄對象的位置,同時資源管理器提供了緩存能力來提高對象訪問的性能。
文檔流轉
CM提供了易于使用的強勁的文檔流轉功能,能支持以內容為基礎的業務流轉,文檔流轉是CM內置的工作流服務,包含了很高的性能、很少的腳本、整合的權限控制列表以及強勁和靈活的工作流建模能力。文檔流轉功能在CM客戶端都得到了體現,并且用戶可以非常容易地定制工作流應用。用戶可以通過C++或Java,JavaBeans來實現工作流的操作和系統管理功能。
更高級的工作流
IBM CM提供了更高級的工作流功能,來實現企業范圍靈活的文檔流轉功能,EIP高級工作流支持并行流轉、數據變量和子進程,并且提供了將CM、CM Ondemand、CM ImagePlus等存儲庫中的內容進行集中流轉的能力。
高層次的應用開發工具
CM整個系統的設計完全圍繞著對象管理來進行的,所以不管是數據模型,還是權限管理以及開發接口,我們都可以通過對象管理的方法DDO/XDO來進行設置和開發,而不需要面向SQL和底層的表結構來進行開發,也不需要針對底層的存儲管理API來進行開發。我們只需要通過統一的、滿足DDO/XDO標準的管理模式和API來進行內容管理,包括編目、權限、存儲、遷移、流程控制等等。
開放標準的全面支持
從Web環境、開發語言到網絡協議、操作系統,乃至數據庫和應用軟件,CM全部遵循通用的國際或行業標準,如開發語言:C++、Java / Java Beans、ActiveX;傳輸連接標準:XML、HTTP、RTSP、JDBC;格式標準:TIFF等所有圖像格式、 HTML、Office、MPEG(1、2、4)、Quicktime、AVI、MP3等;協議標準:CORBA、ODMA、LDAP、SQL和Xquery;系統平臺:Sun Solaris、Windows、AIX、S/390、AS/400;其它數據源:Oracle、DB2、Microsft、Sybase和其它內容管理系統。
實施中的關鍵環節
就目前一般的內容管理應用來看,應該十分重視有效地采集、管理、應用各種非結構化和半結構化的資源,并且將它們與企業的其他業務系統中的各種結構化信息集成起來。
成立專門小組實施內容管理策略
企業在制定內容管理方案時,應考慮到設計、實施、質量監控的問題,人員的組成應涉及到戰略、信息技術、業務等部門,同時還要有企業的主管領導參與,以使決策更加有效和迅速地執行。 還要確立總體的項目計劃、實施策略、項目制度,以指導項目的進行。
梳理所有內容
在這個階段中,要對企業中現有的內容資源進行細致的盤清,確定企業內容資源的價值、數量以及獲取這些內容的難易程度。一般可以從三個方面著手:整理、清查已經公開發布過的各種文檔;確定各種尚未公開發布過的各種知識資源;仔細地清點企業從外界得到的各種內容資源,以此對企業內部的內容資產有一個全面的了解和把握,這是確定內容管理系統內容和技術架構中非常重要的一個環節。
確定內外部內容需求
此間要深入研究企業內部和外部對內容的各種需求,使業務可以得到更好的支撐。對于企業內部來說,從內容管理中得到較多的是企業決策管理者、銷售和市場部門、客戶服務和技術支持部門、研究與開發部門的資源。對于企業外部來說,內容管理應該側重挖掘潛在和現有的客戶、機構和個人投資者,以及能夠對公司產生影響的權威人士等信息。依據他們對于內容的需求,就可以制定出知識在企業業務中的需求藍圖,來描述內容、業務、人員之間的關系。
確定策略確定產品確定實施計劃
滿足各部門和人員的內容需求,需要從內容建設和建設內容管理系統入手。內容建設包括是否購買外部的內容,怎樣將現有的內容重新整理,以滿足新的需求或者滿足不同人員的需求,怎樣進行研發創新等;建設內容管理系統,并采取切實可行的措施,以確保應用。這樣,就首先要確定內容管理系統的建設策略,然后要進行產品和技術方案的選擇。在技術路線確定以后,可根據產品的特點、內容管理的要求,制定出系統的實施計劃,以指導系統的建設。
建設好內容管理系統確保應用
為了確保內容管理系統的有效應用,可以事先選定一二個部門進行試點。此間,要注意多種內容獲取渠道的集成,僅將內容存儲好是遠遠不夠的,用戶應該能通過多種渠道、多種形式獲得所需的信息;還要注意與其它系統進行有效的集成,包括與OA系統、郵件系統、ERP、CRM等業務系統的集成,同時在集成時,還要綜合考慮需求的強烈程度、成本等諸多因素,按收益成本最大化的原則進行集成。
一天,一只兔子在山洞前寫文章,一只狼走了過來,問:“兔子啊,你在干什么?”答曰:“寫文章。”問:“什么題目?”答曰:“《淺談兔子是怎樣吃掉狼的》。”狼哈哈大笑,表示不信,于是兔子把狼領進山洞。過了一會,兔子獨自走出山洞,繼續寫文章。一只野豬走了過來,問:“兔子你在寫什么?”答:“文 章。”問:“題目是什么?”答:“《淺談兔子是如何把野豬吃掉的》。”野豬不信,于是同樣的事情發生。
最后,在山洞里,一只獅子在一堆白骨之間,滿意的剔著牙讀著兔子交給它的文章,題目:“《一只動物,能力大小關鍵要看你的老板是誰》。”
這只兔子有次不小心告訴了他的一個兔子朋友,這消息逐漸在森林中傳播;獅子知道后非常生氣,他告訴兔子:“如果這個星期沒有食物進洞,我就吃你。”于是兔子繼續在洞口寫文章。一只小鹿走過來,“兔子,你在干什么啊?”“寫文章”“什么題目”““《淺談兔子是怎樣吃掉狼的》”。“哈哈,這個事情全森林都知道啊,你別胡弄我了,我是不會進洞的”。“我馬上要退休了,獅子說要找個人頂替我,難道你不想這篇文章的兔子變成小鹿么”。小鹿想了想,終于忍不住誘惑,跟隨兔子走進洞里。過了一會,兔子獨自走出山洞,繼續寫文章。一只小馬走過來,同樣是事情發生了。
最后,在山洞里,一只獅子在一堆白骨之間,滿意的剔著牙讀著兔子交給它的文章,題目是:《如何發展下線動物為老板提供食物》
隨著時間的推移,獅子越長越大,兔子的食物已遠遠不能填飽肚子。一日,他告訴兔子:“我的食物量要加倍,例如:原來4天一只小鹿,現在要2天一只,如果一周之內改變不了局面我就吃你。于是,兔子離開洞口,跑進森林深處,他見到一只狼,“你相信兔子能輕松吃掉狼嗎”。狼哈哈大笑,表示不信,于是兔子把狼領進山洞。過了一會,兔子獨自走出山洞,繼續進入森林深處。這回他碰到一只野豬----“你相信兔子能輕松吃掉野豬嗎” 。野豬不信,于是同樣的事情發生了。原來森林深處的動物并不知道兔子和獅子的故事。
最后,在山洞里,一只獅子在一堆白骨之間,滿意的剔著牙讀著兔子交給它的文章,題目是:《如何實現由坐商到行商的轉型為老板提供更多的食物》
時間飛快,轉眼之間,兔子在森林里的名氣越來越大,因為大家都知道它有一個很歷害的老板。這只小兔開始橫行霸道,欺上欺下,沒有動物敢惹。它時時想起和烏龜賽跑的羞辱。它找到烏龜說:“三天之內,見我老板!”揚長而去。烏龜難過的哭了,這時卻碰到了一位獵人,烏龜把這事告訴了他,獵人哈哈大笑。于是森林里發生了一件重大事情,獵人披著獅子皮和烏龜一起在吃兔子火鍋。
地下丟了半張紙片歪歪扭扭的寫著:山外青山樓外樓,強中還有強中手啊!!
在很長一段時間里森林里恢復了往日的寧靜,兔子吃狼的故事似乎快要被大家忘記了,不過一只年輕的老虎在聽說了這個故事后,被激發了靈感。于是他抓住了一只羚羊,對羚羊說,如果你可以象以前的兔子那樣為我帶來食物那我就不吃你。于是,羚羊無奈的答應了老虎,而老虎也悠然自得的進了山洞。可是三天過去了,也沒有見羚羊領一只動物進洞。他實在憋不住了,想出來看看情況。羚羊早已不在了,他異常憤怒。
正在他暴跳如雷的時候突然發現了羚羊寫的一篇文章,題目是:《想要做好老板先要懂得怎樣留住員工》。
1)機器思維
優秀的程序員最擅長和電腦程序打交道,并通過代碼去控制反饋。而管理需要和人打交道,需要收集人的反饋。電腦是按邏輯來執行的,而人卻要復雜很多,特別是團隊中有女性成員,挑戰難度就更大。由于長期和電腦接觸,很多程序員缺乏和別人溝通的技巧,或者說情商相對較低。這在管理上是比較致命的缺點。
2)BUG思維
優秀的程序員追求完美,看自己或者別人代碼時第一反應是看什么地方可能有BUG, 管理時如果帶著BUG思維,就會只看到別人的不足和錯誤,而不去表揚其有進步的地方。(完美思維的壞處還有一個,就是過于關注細節)如果方向和前提有問題,過于關注細節反而會帶來延誤。
3)工匠思維
程序員靠手藝吃飯,創業總是會碰到各種困難,在碰到困境的時候程序員出身的創業者是有退路的,大不了我再回去寫程序搞技術好了。創業最需要的就是堅持,需要一種永不言棄的精神氣,不能堅持到底,也就不能收獲果實。
4)大俠思維
以技術創業起家的容易迷信技術,忽視市場,忽視管理,總以為只有自己的是最好的。遺憾的是技術變遷實在太快,一時的先進不能代表永遠的先進。先進的技術也不一定就是致勝的法寶。
5)邊界思維
程序員設計代碼和系統時,常常會考慮要處理邊界和異常。反映到思維習慣上,就是遇到問題,就會全面的思考各種情況。這是很好的優點,但做事業時,這有時候反而會是缺點。
上面五類有不少具體例子,大家也可以看看自己的思維習慣里面是不是這樣?
習慣是很難改變的,最好的處理方式是找到搭檔,能彌補自己的不足,這樣成功的概率才會加大。
來自:http://www.blog.edu.cn/user3/KingThie/archives/2007/1822533.shtml
在eclipse中,啟動run on server時報錯:
Resource is out of sync with the file system: '/Test_1_Struts_Spring_Hibernate/WebContent/WEB-INF/.struts-config.xml.strutside'.
查閱資料后發現這是文件系統不同步的問題,是因為在eclipse之外對工程中的resource進行修改引起的;但是,有時沒有在eclipse之外進行修改,也會報類似的錯誤。
解決辦法:需要手動刷新一下資源管理器。
(1)在eclipse中,工程目錄右鍵,選擇F5(refresh)
(2)設置eclipse自動刷新。
通過Window->Preferences->General->Workspace,選中Refresh automatically。
來自:
http://hi.baidu.com/proglife/blog/item/0c14d0d9ad404e2910df9b3a.html
Oracle提供了兩個腳本dbstart和dbshut用來啟動和關閉數據庫.
這兩個腳本首先讀取oratab(/etc/oratab)文件來決定哪個數據庫是需要自動啟動和關閉,然后啟動和關閉那些數據庫,
oratab文件通過root.sh創建.
[oracle@chicago oracle]$ cat /etc/oratab
#
# This file is used by ORACLE utilities. It is created by root.sh
# and updated by the Database Configuration Assistant when creating
# a database.
# A colon, ':', is used as the field terminator. A new line terminates
# the entry. Lines beginning with a pound sign, '#', are comments.
#
# Entries are of the form:
# $ORACLE_SID:$ORACLE_HOME:<N|Y>:
#
# The first and second fields are the system identifier and home
# directory of the database respectively. The third filed indicates
# to the dbstart utility that the database should , "Y", or should not,
# "N", be brought up at system boot time.
#
# Multiple entries with the same $ORACLE_SID are not allowed.
#
#
orcl:/u01/app/oracle/oracle/product/10.2.0/db_1:Y
不過,dbstart/dbshut腳本中都包含有錯誤.
需要修改ORACLE_HOME_LISTNER=$ORACLE_HOME
[oracle@chicago oracle]$ dbstart
ORACLE_HOME_LISTNER is not SET, unable to auto-start Oracle Net Listener
Usage: /u01/app/oracle/oracle/product/10.2.0/db_1/bin/dbstart ORACLE_HOME
[oracle@chicago oracle]$ vi $ORACLE_HOME/bin/dbstart
:
#
# $Id: dbstart.sh.pp 25-may-2005.14:52:00 vikrkuma Exp $
# Copyright (c) 1991, 2005, Oracle. All rights reserved.
#
###################################
#
# usage: dbstart $ORACLE_HOME
#
# This script is used to start ORACLE from /etc/rc(.local).
# It should ONLY be executed as part of the system boot procedure.
#
# This script will start all databases listed in the oratab file
# whose third field is a "Y". If the third field is set to "Y" and
# there is no ORACLE_SID for an entry (the first field is a *),
# then this script will ignore that entry.
#
# This script requires that ASM ORACLE_SID's start with a +, and
# that non-ASM instance ORACLE_SID's do not start with a +.
#
# If ASM instances are to be started with this script, it cannot
# be used inside an rc*.d directory, and should be invoked from
# rc.local only. Otherwise, the CSS service may not be available
# yet, and this script will block init from completing the boot
# cycle.
#
# If you want dbstart to auto-start a single-instance database that uses
# an ASM server that is auto-started by CRS (this is the default behavior
# for an ASM cluster), you must change the database's ORATAB entry to use
# a third field of "W" and the ASM's ORATAB entry to use a third field of "N".
# These values specify that dbstart auto-starts the database only after
# the ASM instance is up and running.
#
# Note:
# Use ORACLE_TRACE=T for tracing this script.
#
# The progress log for each instance bringup plus Error and Warning message[s]
# are logged in file $ORACLE_HOME/startup.log. The error messages related to
# instance bringup are also logged to syslog (system log module).
# The Listener log is located at $ORACLE_HOME_LISTNER/listener.log
#
# To configure:
# 1) Set ORATAB:
# On Solaris
# ORATAB=/var/opt/oracle/oratab
# All other UNIX platforms
# ORATAB=/etc/oratab
#
# 2) Update $ORATAB/oratab with Database Instances that need to be started up.
# Entries are of the form:
# $ORACLE_SID:$ORACLE_HOME:<N|Y|W>:
# An example entry:
# main:/usr/lib/oracle/emagent_10g:Y
#
# Overall algorithm:
# 1) Bring up all ASM instances with 'Y' entry in status field in oratab entry
# 2) Bring up all Database instances with 'Y' entry in status field in
# oratab entry
# 3) If there are Database instances with 'W' entry in status field
# then
# iterate over all ASM instances (irrespective of 'Y' or 'N') AND
# wait for all of them to be started
# fi
# 4) Bring up all Database instances with 'W' entry in status field in
# oratab entry
#
#####################################
LOGMSG="logger -puser.alert -s "
trap 'exit' 1 2 3
# for script tracing
case $ORACLE_TRACE in
T) set -x ;;
esac
# Set path if path not set (if called from /etc/rc)
case $PATH in
"") PATH=/bin:/usr/bin:/etc
export PATH ;;
esac
# Save LD_LIBRARY_PATH
SAVE_LLP=$LD_LIBRARY_PATH
# First argument is used to bring up Oracle Net Listener
ORACLE_HOME_LISTNER=$ORACLE_HOME
if [ ! $ORACLE_HOME_LISTNER ] ; then
echo "ORACLE_HOME_LISTNER is not SET, unable to auto-start Oracle Net Listener"
echo "Usage: $0 ORACLE_HOME"
else
LOG=$ORACLE_HOME_LISTNER/listener.log
# Start Oracle Net Listener
if [ -x $ORACLE_HOME_LISTNER/bin/tnslsnr ] ; then
echo "$0: Starting Oracle Net Listener" >> $LOG 2>&1
"/u01/app/oracle/oracle/product/10.2.0/db_1/bin/dbstart" 461L, 13926C written
[oracle@chicago oracle]$ dbstart
Processing Database instance "orcl": log file /u01/app/oracle/oracle/product/10.2.0/db_1/startup.log
[oracle@chicago oracle]$ dbshut
ORACLE_HOME_LISTNER is not SET, unable to auto-stop Oracle Net Listener
Usage: /u01/app/oracle/oracle/product/10.2.0/db_1/bin/dbshut ORACLE_HOME
Processing Database instance "orcl": log file /u01/app/oracle/oracle/product/10.2.0/db_1/shutdown.log
[oracle@chicago oracle]$ vi $ORACLE_HOME/bin/dbshut
:
#
# $Id: dbshut.sh.pp 11-may-2005.19:37:00 vikrkuma Exp $
# Copyright (c) 1991, 2005, Oracle. All rights reserved.
#
###################################
#
# usage: dbshut $ORACLE_HOME
#
# This script is used to shutdown ORACLE from /etc/rc(.local).
# It should ONLY be executed as part of the system boot procedure.
#
# This script will shutdown all databases listed in the oratab file
# whose third field is a "Y" or "W". If the third field is set to "Y" and
# there is no ORACLE_SID for an entry (the first field is a *),
# then this script will ignore that entry.
#
# This script requires that ASM ORACLE_SID's start with a +, and
# that non-ASM instance ORACLE_SID's do not start with a +.
#
# Note:
# Use ORACLE_TRACE=T for tracing this script.
# Oracle Net Listener is also shutdown using this script.
#
# The progress log for each instance shutdown is logged in file
# $ORACLE_HOME/shutdown.log.
#
# To configure:
# 1) Set ORATAB:
# On Solaris
# ORATAB=/var/opt/oracle/oratab
# All other UNIX platforms
# ORATAB=/etc/oratab
#
# 2) Update $ORATAB/oratab with Database Instances that need to be shutdown.
# Entries are of the form:
# $ORACLE_SID:$ORACLE_HOME:<N|Y>:
# An example entry:
# main:/usr/lib/oracle/emagent_10g:Y
#
# Note:
# Use ORACLE_TRACE=T for tracing this script.
# Oracle Net Listener is NOT shutdown using this script.
#
# The progress log for each instance shutdown is logged in file
# $ORACLE_HOME/shutdown.log.
#
# To configure:
# 1) Set ORATAB:
# On Solaris
# ORATAB=/var/opt/oracle/oratab
# All other UNIX platforms
# ORATAB=/etc/oratab
#
# 2) Update $ORATAB/oratab with Database Instances that need to be shutdown.
# Entries are of the form:
# $ORACLE_SID:$ORACLE_HOME:<N|Y>:
# An example entry:
# main:/usr/lib/oracle/emagent_10g:Y
#
#####################################
trap 'exit' 1 2 3
case $ORACLE_TRACE in
T) set -x ;;
esac
# Set path if path not set (if called from /etc/rc)
case $PATH in
"") PATH=/bin:/usr/bin:/etc
export PATH ;;
esac
# Save LD_LIBRARY_PATH
SAVE_LLP=$LD_LIBRARY_PATH
# The this to bring down Oracle Net Listener
ORACLE_HOME_LISTNER=$ORACLE_HOME
if [ ! $ORACLE_HOME_LISTNER ] ; then
echo "ORACLE_HOME_LISTNER is not SET, unable to auto-stop Oracle Net Listener"
echo "Usage: $0 ORACLE_HOME"
else
LOG=$ORACLE_HOME_LISTNER/listener.log
# Stop Oracle Net Listener
if [ -f $ORACLE_HOME_LISTNER/bin/tnslsnr ] ; then
"/u01/app/oracle/oracle/product/10.2.0/db_1/bin/dbshut" 246L, 6592C written
[oracle@chicago oracle]$ dbstart
Processing Database instance "orcl": log file /u01/app/oracle/oracle/product/10.2.0/db_1/startup.log
[oracle@chicago oracle]$ dbshut
Processing Database instance "orcl": log file /u01/app/oracle/oracle/product/10.2.0/db_1/shutdown.log
[oracle@chicago oracle]$
來自:http://benbo.itpub.net/post/26034/311306
摘要: Ubuntu 8.04 LTS(長期支持版)免費光盤已經接受預定! https://shipit.ubuntu.com/有刻錄機的最好自己刻盤,體諒人家.
總綱:分以下幾部分工作 (更新截至4月27日,決定停止更新,文章寫長了就沒意思了,眼花,啰嗦)
作者 pengkuny
原始鏈接 http://www.pengkuny.com/post/Install_Ubuntu8.04.html
...
閱讀全文