使用Acegi保護(hù)JSF應(yīng)用程序
如何使用 Acegi 保護(hù)在 servlet 容器中運(yùn)行的 JavaServer Faces (JSF) 應(yīng)用程序。本文首先解釋 Acegi 針對(duì)此目標(biāo)提供的特性,并澄清一些關(guān)于使用 Acegi和JSF 的常見(jiàn)誤解。然后提供一個(gè)簡(jiǎn)單的 web.xml 文件,可以用來(lái)部署 Acegi,從而保護(hù) JSF應(yīng)用程序。然后深入探討 Acegi和JSF 組件,了解在部署 web.xml 文件和用戶訪問(wèn) JSF應(yīng)用程序時(shí)所發(fā)生的事件。本文最后提供了一個(gè)由 Acegi 保護(hù)的示例 JSF應(yīng)用程序。
無(wú)需編寫(xiě) Java 代碼即可添加安全性
回顧一下本系列的第一個(gè)示例 Acegi 應(yīng)用程序(請(qǐng)參閱 第 1 部分 中的 “一個(gè)簡(jiǎn)單 Acegi 應(yīng)用程序” 一節(jié))。該應(yīng)用程序使用 Acegi 提供了以下安全特性:
◆當(dāng)一個(gè)未經(jīng)驗(yàn)證的用戶試圖訪問(wèn)受保護(hù)的資源時(shí),提供一個(gè)登錄頁(yè)面。
◆將授權(quán)用戶直接重定向到所需的受保護(hù)資源。
◆如果用戶未被授權(quán)訪問(wèn)受保護(hù)資源,提供一個(gè)訪問(wèn)拒絕頁(yè)面。
回想一下,您無(wú)需編寫(xiě)任何 Java 代碼就能獲得這些特性。只需要對(duì) Acegi 進(jìn)行配置。同樣,在 JSF應(yīng)用程序中,無(wú)需編寫(xiě)任何 Java 代碼,也應(yīng)該能夠從 Acegi 實(shí)現(xiàn)相同的特性。
澄清誤解
其他一些作者似乎認(rèn)為將 Acegi 與 JSF 集成需要 JSF應(yīng)用程序提供登錄頁(yè)面(參見(jiàn) 參考資料)。這種觀點(diǎn)并不正確。在需要時(shí)提供登錄頁(yè)面,這是 Acegi 的職責(zé)。確保登錄頁(yè)面在安全會(huì)話期間只出現(xiàn)一次,這也是 Acegi 的職責(zé)。然后,經(jīng)過(guò)身份驗(yàn)證和授權(quán)的用戶可以訪問(wèn)一個(gè)受保護(hù)資源,無(wú)需重復(fù)執(zhí)行登錄過(guò)程。
如果使用 JSF 提供登錄頁(yè)面,將會(huì)發(fā)生兩個(gè)主要的問(wèn)題:
◆當(dāng)需要時(shí),沒(méi)有利用 Acegi 的功能提供登錄頁(yè)面。必須編寫(xiě) Java 代碼實(shí)現(xiàn)所有邏輯來(lái)提供登錄頁(yè)面。
◆至少需要編寫(xiě)一些 Java 代碼將用戶憑證(用戶名和密碼)從 JSF 的登錄頁(yè)面移交到 Acegi。
Acegi 的目的是避免編寫(xiě) Java 安全代碼。如果使用 JSF 提供登錄頁(yè)面,則沒(méi)有實(shí)現(xiàn)這一用途,并且會(huì)引發(fā)一系列其他 JSF-Acegi 集成問(wèn)題,所有這些問(wèn)題都源于 “Acegi 是用來(lái)提供可配置安全性” 這一事實(shí)。如果試圖使用 JSF 來(lái)完成 Acegi 的工作,將會(huì)遇到麻煩。
本文余下部分將解釋并演示獨(dú)立于 Acegi 的 JSF應(yīng)用程序開(kāi)發(fā),并在稍后配置 Acegi 以保護(hù) JSF應(yīng)用程序 — 無(wú)需編寫(xiě)任何 Java 代碼。首先看一下 web.xml 文件,可以部署該文件保護(hù) JSF應(yīng)用程序。
部署 Acegi 保護(hù) JSF應(yīng)用程序
清單 1 展示了一個(gè) web.xml 文件(通常稱為部署描述符),可以使用這個(gè)文件部署 Acegi,從而保護(hù)運(yùn)行在 servlet 容器(比如 Apache Tomcat)中的 JSF應(yīng)用程序:
清單 1. 用于部署 Acegi 和 servlet 容器中的 JSF 的 web.xml 文件
- <?xml version="1.0"?>
- <!DOCTYPE web-app PUBLIC-//Sun Microsystems, Inc.//DTD
Web Application 2.3//EN http://java.sun.com/dtd/web-app_2_3.dtd">- <web-app>
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>/WEB-INF/acegi-config.xml</param-value>
- </context-param>
- <context-param>
- <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
- <param-value>server</param-value>
- </context-param>
- <context-param>
- <param-name>javax.faces.CONFIG_FILES</param-name>
- <param-value>/WEB-INF/faces-config.xml</param-value>
- </context-param>
- <listener>
- <listener-class>
- org.springframework.web.context.ContextLoaderListener
- </listener-class>
- </listener>
- <listener>
- <listener-class>
- com.sun.faces.config.ConfigureListener
- </listener-class>
- </listener>
- <!-- Faces Servlet -->
- <servlet>
- <servlet-name>Faces Servlet</servlet-name>
- <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
- <load-on-startup> 1 </load-on-startup>
- </servlet>
- <!-- Faces Servlet Mapping -->
- <servlet-mapping>
- <servlet-name>Faces Servlet</servlet-name>
- <url-pattern>*.faces</url-pattern>
- </servlet-mapping>
- <!-- Acegi filter configuration -->
- <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>
- <!-- Acegi Filter Mapping -->
- <filter-mapping>
- <filter-name>Acegi Filter Chain Proxy</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- </web-app>
注意,清單 1 包含以下標(biāo)記:
◆3 個(gè) <context-param> 標(biāo)記
◆2 個(gè) <listener> 標(biāo)記
◆1 個(gè) <filter> 標(biāo)記
◆1 個(gè) <servlet> 標(biāo)記
◆1 個(gè) <servlet-mapping> 標(biāo)記
◆1 個(gè) <filter-mapping> 標(biāo)記
閱讀該文件,了解每個(gè)標(biāo)記在 JSF-Acegi 應(yīng)用程序中的用途。
向 Acegi和JSF 提供上下文參數(shù)
清單 1 中的每個(gè)
JSF 需要 javax.faces.STATE_SAVING_METHOD 和 javax.faces.CONFIG_FILES 參數(shù)。javax.faces.STATE_SAVING_METHOD 參數(shù)指定希望在客戶機(jī)還是服務(wù)器上存儲(chǔ) JSF 頁(yè)面-視圖狀態(tài)。Sun 的參考實(shí)現(xiàn)的默認(rèn)行為是將 JSF 視圖存儲(chǔ)在服務(wù)器上。
javax.faces.CONFIG_FILES 參數(shù)指定 JSF 需要的配置文件的位置。JSF 配置文件的詳細(xì)信息不屬于本文討論的范圍(參見(jiàn) 參考資料,獲取涉及該主題的資源鏈接)。
為 Acegi和JSF 配置偵聽(tīng)器
現(xiàn)在看一下 清單 1 中的 2 個(gè)
◆啟動(dòng) JSP 或 servlet 應(yīng)用程序時(shí)servlet容器創(chuàng)建一個(gè)新的 servlet 上下文。每當(dāng) JSP 或 servlet 應(yīng)用程序啟動(dòng)時(shí),就會(huì)觸發(fā)此事件。
◆servlet 容器創(chuàng)建一個(gè)新的 servlet 請(qǐng)求對(duì)象。每當(dāng)容器從客戶機(jī)收到一個(gè) HTTP 請(qǐng)求時(shí),此事件就會(huì)發(fā)生。
◆建立一個(gè)新的 HTTP 會(huì)話。當(dāng)請(qǐng)求客戶機(jī)建立一個(gè)與 servlet 容器的會(huì)話時(shí),此事件就會(huì)發(fā)生。
◆一個(gè)新屬性被添加到 servlet 上下文、servlet 請(qǐng)求和 HTTP 會(huì)話對(duì)象。
◆servlet 上下文、servlet 請(qǐng)求或 HTTP 會(huì)話對(duì)象的一個(gè)現(xiàn)有屬性被修改或刪除。
例如,Spring Framework 實(shí)現(xiàn)一個(gè) javax.servlet.ServletContextListener servlet 接口。實(shí)現(xiàn)此接口的 spring 類是 org.springframework.web.context.ContextLoaderListener。注意,這是 清單 1 的第一個(gè)
類似地,JSF 實(shí)現(xiàn)一個(gè) com.sun.faces.config.ConfigureListener 類,該類實(shí)現(xiàn)一些事件-偵聽(tīng)接口??梢栽?清單 1 的第二個(gè)
本文稍后將解釋不同的事件-偵聽(tīng)器接口,以及 Acegi和JSF 事件-偵聽(tīng)器類內(nèi)部執(zhí)行的處理(請(qǐng)參閱 “啟動(dòng) JSF-Acegi 應(yīng)用程序” 和 “處理對(duì)受 Acegi 保護(hù)的 JSF 頁(yè)面的請(qǐng)求”)。
配置和映射 servlet 過(guò)濾器
現(xiàn)在看一下 清單 1 中的
請(qǐng)注意 清單 1 中的
還需注意,清單 1 的
清單 1 中的
配置 JSF servlet
web.xml 文件中的
現(xiàn)在,您已經(jīng)看到,web.xml 文件要部署 Acegi 以保護(hù) JSF 應(yīng)用程序所需的所有標(biāo)記。您已經(jīng)了解了偵聽(tīng)器、過(guò)濾器和 servlet 如何相互協(xié)作。從這里的討論中可以看出,如果在 servlet 容器中部署 清單 1 中的 web.xml 文件,Acegi和JSF 都試圖在兩種情形下進(jìn)行一些處理:
◆當(dāng)啟動(dòng)應(yīng)用程序時(shí)
◆當(dāng)應(yīng)用程序收到對(duì) JSF 頁(yè)面的請(qǐng)求時(shí)
【編輯推薦】