在 JSF 應用程序中使用安全 bean 的最簡單方法就是,執行 第 3 部分 的清單 4 中介紹的五個步驟。在第 3 部分中,我從 servlet 上下文中取出了 Spring 框架的 Web 應用程序上下文對象。可以在以後使用 Web 應用程序上下文安全地訪問 bean。下面的 清單 1 演示瞭如何在 JSF 頁面中使用 Web 應用程序上下文:
清單 1. 從 servlet 上下文提取 Web 應用程序上下文,並將其用於 JSF 頁面
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@page import="sample.CatalogBean"%> <%@page import="org.springframework.web.context.support.WebApplicationContextUtils" %> <%@page import="org.springframework.web.context.WebApplicationContext" %> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <html> <head> <title>Acegi simple method security application: TEST PAGE</title> </head> <body> <f:view> <h2> <h:outputText value="Protected Resource 1:"/> </h2> <% try { WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext( this.getServletConfig().getServletContext()); CatalogBean privateCatalog = (CatalogBean) webApplicationContext.getBean("privateCatalog"); String privateData = catalog.getData(); request.setAttribute("privateData", privateData); } catch (Exception e) { } %> <h3> <h:outputText value="#{privateData}"/> </h3> </f:view> </body> </html> |
可以看到,清單 1 使用名爲 WebApplicationContextUtils
的類提取 Web 應用程序上下文的實例。WebApplicationContextUtils
是 Spring 提供的一個工具類。
在得到 Web 應用程序上下文之後,能夠調用它的 getBean()
方法得到在 Acegi 配置文件中配置的任何 bean。然後可以調用該 bean 的 getter 方法,並將 getter 方法返回的數據以參數的形式存儲在 servlet 請求對象中。這些步驟允許 清單 1 中的 <outputText>
標籤向用戶提供數據。
像 清單 1 那樣直接管理 bean 數據雖然簡單,但並不可取。這個方法違反了 JSF 的模型-視圖-控制器(MVC)架構(請參閱 參考資料),MVC 架構要求使用模型 bean 保存應用程序數據。所以最好不要在 JSF 應用程序中使用這種策略,除非在非常簡單的情況下。
Bean 依賴關係
Spring 的 IOC 框架通過表示 bean 之間的依賴關係,提供了一種管理模型 bean 的有用方式。我在 第 1 部分的 “架構和組件” 小節解釋了 IOC 中的 bean 依賴關係概念。
JSF 提供了管理應用程序模型 bean 的豐富功能。這類 bean — 稱爲託管 bean — 被應用於大多數 JSF 應用程序,所以大多數實際的 JSF 應用程序都需要保護託管 bean。
本文餘下部分將討論在 JSF 應用程序中使用安全 bean 的兩個策略:
- 使用 Acegi 保護 JSF 託管 bean
- 使用直接由 Acegi 在 JSF 標籤中保護的 反轉控制(IOC)bean
請看 清單 2 所示的 JSF 頁面,其中使用了一個名爲 catalog
的 JSF 託管 bean:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <html> <head> <title>JSF Acegi simple method security application: TEST PAGE</title> </head> <body> <f:view> <h2> <h:outputText value="Protected Resource 1:"/> </h2> </br> <h3> <h:outputText value="#{catalog.publicData}"/> </br> <h:outputText value="#{catalog.privateData}"/> </h3> </f:view> </body> </html> |
清單 2 使用了兩個 JSF <outputText>
標籤。第一個 <outputText>
標籤有一個 #{catalog.publicData}
的 value
屬性,第二個標籤有一個 #{catalog.privateData}
的 value
屬性。這兩個標籤使用 catalog
bean 的 publicData
和 privateData
屬性,它們分別提供公共和私有的編目數據。
在 第 3 部分 的 “訪問執行過代理的 Java 對象” 小節中,我配置了兩個 Acegi bean,分別名爲 publicCatalog
和 privateCatalog
。現在我要將第 3 部分的 publicCatalog
bean(不受保護的供公共訪問的 bean)映射到 catalog
bean 的 publicData
屬性。類似的,將第 3 部分的 privateCatalog
(在 第 3 部分 的清單 3 中配置的受保護且執行過代理的 bean)映射到上面 清單 2 的託管 bean catalog
的 privateData
屬性。映射完成後,catalog
bean 就會充當 JSF 編目應用程序的公共和私有數據的包裝器。
清單 3 演示瞭如何定義 catalog
bean,以便將它的 publicData
和 privateData
屬性分別映射到 Acegi 的 publicCatalog
和 privateCatalog
bean:
清單 3. 將 catalog
的屬性映射到 Acegi 的 bean
<?xml version="1.0"?> <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd"> <faces-config> <managed-bean> <managed-bean-name>catalog</managed-bean-name> <managed-bean-class>sample.Catalog</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>publicData</property-name> <value>#{publicCatalog.data}</value> </managed-property> <managed-property> <property-name>privateData</property-name> <value>#{privateCatalog.data}</value> </managed-property> </managed-bean> <application> <variable-resolver> org.springframework.web.jsf.DelegatingVariableResolver </variable-resolver> </application> </faces-config> |
清單 3 實際上演示了 JSF 的一個配置文件。它的根標籤是 <faces-config>
,這是大多數 JSF 程序員都熟悉的標籤。根 <faces-config>
標籤包含兩個子標籤,名爲 <managed-bean>
和 <application>
。現在我要詳細解釋這兩個標籤。
清單 3 的 <managed-bean>
標籤定義了 catalog
bean 和它的屬性。<managed-bean>
標籤有三個子標籤 — <managed-bean-name>
、<managed-bean-class>
和 <managed-bean-scope>
— 以及兩個 <managed-property>
標籤。前兩個子標籤分別定義了 bean 的名稱(catalog
)和類(sample.Catalog
)。
清單 3 中的每個 <managed-property>
標籤定義 catalog
bean 的一個屬性。每個 <managed-property>
標籤有兩個子標籤 — <property-name>
和 <value>
— 分別定義了屬性的名稱和值。從 清單 3 可以看出,第一個屬性的名稱是 publicData
,它的值是 #{publicCatalog.data}
。類似的,第二個屬性的名稱是 privateData
,它的值是 #{privateCatalog.data}
。
這兩個值實際上是表達式,分別解析爲其他託管 bean 的屬性。第一個表達式(#{publicCatalog.data}
)商業智能 publicCatalog
bean 的 data 屬性。類似的,第二個表達式(#{privateCatalog.data}
)解析爲 privateCatalog
bean 的 data 屬性。
JSF 提供了一種機制,能夠將 #{publicData.data}
這樣的表達式解析爲實際的託管 bean 實例。我將會討論 JSF 的表達式-解析(expression-resolving)機制(在 “定義表達式商業智能器” 小節)。
但是,這裏有一個問題。清單 3 的 JSF 配置文件不包含名爲 publicCatalog
和 privateCatalog
的託管 bean。我在 第 3 部分 的 “訪問執行過代理的 Java 對象” 小節中配置了 publicCatalog
和 privateCatalog
IOC bean(不是 JSF 託管 bean)。所以,JSF 表達式-解析機制必須能夠解析爲 Acegi 的 IOC bean。
JSF 的 javax.faces.el.VariableResolver
類是默認的表達式解析器,能夠將表達式解析爲 JSF 的託管 bean。但是,VariableResolver
不能解析爲 IOC bean。
JSF 提供了一種擴展機制,允許應用程序開發人員編寫自己的表達式解析器。Spring 在名爲 org.springframework.web.jsf.DelegatingVariableResolver
的類中提供了 JSF 表達式解析器。DelegatingVariableResolver
類能夠將表達式解析爲 IOC bean。DelegatingVariableResolver
也用默認的 VariableResolver
將表達式解析爲 JSF 託管 bean。
要使用 Spring 的 DelegatingVariableResolver
,必須在 JSF 的配置文件中配置它。這正是在 清單 3 中包含 <application>
標籤的目的(清單 4 顯示了這個標籤,用於快速參考):
<faces-config> .......... <application> <variable-resolver> org.springframework.web.jsf.DelegatingVariableResolver </variable-resolver> </application> </faces-config> |
清單 4 中的 <application>
標籤只包含一個子標籤,名爲 <variable-resolver>
,用於爲 JSF 應用程序配置外部解析器。<variable-resolver>
標籤包裝了 Spring 解析器類的名稱(org.springframework.web.jsf.DelegatingVariableResolver
),負責將表達式解析爲 IOC bean。
前面已經看到了如何配置 JSF 應用程序以使用 Acegi 的 IOC bean。現在可以看看剛剛配置的三個 bean。
清單 5 顯示了 Catalog
類的實現,它的實例 — 名爲 catalog
— 被配置爲 JSF 中的託管 bean:
package sample; public class Catalog { private String publicData = null; private String privateData = null; public Catalog () { } public void setPublicData(String publicData) { this.publicData = publicData; } public void setPrivateData(String privateData) { this.privateData = privateData; } public String getPublicData() { return publicData; } public String getPrivateData() { return privateData; } }//Catalog |
從 清單 5 可以看出,Catalog
類只包含 publicData
和 privateData
屬性的 getter 和 setter 方法。JSF 框架將會調用 getter 和 setter 方法,我將在下一節解釋這一點。
現在看一下兩個 IOC bean(publicCatalog
和 privateCatalog
)的實現,如 清單 6 所示:
清單 6. publicCatalog
和 privateCatalog
IOC bean
//PublicCatalog package sample; public class PublicCatalog implements CatalogBean { public PublicCatalog () { } public String getData() { return "This is public catalog data"; } } //PrivateCatalog package sample; public class PrivateCatalog implements CatalogBean { public PrivateCatalog () { } public String getData() { return "This is private catalog data"; } } |
在 清單 6 可以看到,我在兩個 IOC bean 中對實際的公共和私有數據進行了硬編碼。在真實的應用程序中,這些 bean 將會從數據庫讀取數據。
現在已經看到了保護 JSP 託管 bean 中包裝的數據所需要的所有組件和配置,下面看一下 JSF 和 Acegi 如何協作使用這些組件和配置。
當用戶試圖訪問 清單 2 的 JSF 頁面時,就會發生 圖 1 所示的一系列事件。我列出了支持 Acegi URL 安全性和 JSF 應用程序中的 bean 安全性的所有事件。
圖 1 所示的事件順序如下:
- 用戶訪問 JSF 頁面。
- Acegi 檢查該用戶是否有權訪問該 JSF 頁面。(請參閱 第 4 部分 的 “處理對受 Acegi 保護的 JSF 頁面的請求” 一節。)
- 如果授權過程成功,則將控制權轉到 faces servlet,由它準備提供 JSF 頁面。
- 在準備期間,JSF 找到 清單 2 所示的 JSF 頁面中的
catalog
bean。 - JSF 檢查 清單 3 所示的配置文件,查找
catalog
bean 的定義並將其實例化。JSF 還在配置文件中檢查catalog
bean 的屬性。它發現catalog
bean 的publicData
和privateData
屬性被映射到publicCatalog
和privateCatalog
bean,清單 3 中未將這兩個 bean 配置爲 JSF 託管 bean。 - JSF 使用 Spring 的
DelegatingVariableResolver
變量解析器(在 清單 4 中配置)解析publicCatalog
和privateCatalog
bean。 - JSF 使用 Acegi 調用
publicCatalog
和privateCatalog
beans 的 getter 方法獲取公共和私有數據。 - Acegi 再次執行對訪問 bean 的授權過程。(請參閱 第 3 部分 對 Java 對象進行這一授權過程的詳細討論。)
- 如果 Acegi 發現用戶得到授權可以訪問 bean,就會調用 getter 方法,獲取公共和私有數據,並將數據提供給 JSF。
- JSF 調用
catalog
bean 的 setter 方法在catalog
bean 中設置公共和私有數據。 - JSF 執行其生命週期並提供 JSF 頁面。
具有安全託管 bean 的 JSF-Acegi 示例應用程序
本文附帶了一個名爲 JSFAcegiSampleWithSecureManagedBeans
的示例應用程序(請參閱 下載)。它使用前面兩節介紹的技術保護對 JSF 託管 bean 內包裝的數據的訪問。
要部署示例應用程序,請執行 第 1 部分 的 “部署和運行應用程序” 小節中的兩個步驟。還需要從 Sun 的 JSF 網站(請參閱 參考資料)下載 jsf-1_1_01.zip 並解壓。將 jsf-1.1.X.zip 中的所有文件複製到 JSFAcegiSampleWithSecureManagedBeans
應用程序的 WEB-INF/lib 文件夾中。還需要下載 cglib-full-2.0.2.jar
文件(在本系列的 第 3 部分 中用到過)並將它複製到 JSFAcegiSampleWithSecureManagedBeans
應用程序的 WEB-INF/lib
文件夾中。從瀏覽器訪問 http://localhost:8080/JSFAcegiSampleWithSecureManagedBeans 可以調用示例應用程序。
直接在 JSF 應用程序中使用 Acegi 的 IOC bean
您已經學習瞭如何將 JSF 託管 bean 的屬性映射到 Acegi 的 IOC bean,如何配置 DelegatingVariableResolver
以將表達式解析爲 IOC bean。還看到了 JSF 和 Acegi 如何協作以保護對 bean 數據的訪問。
除此之外,還能夠直接在 JSF 頁面中使用 IOC bean,如 清單 7 中的 JSF 頁面所示:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <html> <head> <title>JSF Acegi simple method security application: TEST PAGE</title> </head> <body> <f:view> <h2> <h:outputText value="Protected Resource 1:"/> </h2> </br> <h3> <h:outputText value="#{publicCatalog.data}"/> </br> <h:outputText value="#{privateCatalog.data}"/> </h3> </f:view> </body> </html> |
清單 7 與 清單 2 中的 JSF 頁面類似。惟一的區別在於 <outputText>
標籤的 value
屬性。在 清單 2 中,value
屬性引用 catalog
,後者是一個 JSF 託管 bean。在 清單 7 中,value
屬性直接引用 IOC bean(即 publicCatalog
和 privateCatalog
)。這意味着當用戶訪問 清單 7 的 JSF 頁面時,JSF 直接用 Spring 的 DelegatingVariableResolver
解析 Acegi IOC。
請注意,爲了解析 JSF 頁面中使用的 IOC bean, DelegatingVariableResolver
的工作方式與我在討論 圖 1 時說明的方式相同。
圖 2 演示了用戶訪問 清單 7 中的 JSF 頁面時發生的事件順序。
圖 2. JSF 和 Acegi 組件協作提供帶有安全 IOC bean 的 JSF 頁面
- 用戶訪問 JSF 頁面。
- Acegi 檢查用戶是否有權訪問該 JSF 頁面。
- 如果授權過程成功,則將控制權轉移給 JSF,由 JSF 準備提供 JSF 頁面。
- 在準備期間,JSF 找到 清單 7 的 JSF 頁面中的
publicCatalog
和privateCatalog
bean。 - JSF 檢查 清單 3 的配置文件,發現
publicCatalog
和privateCatalog
bean 沒有在配置文件中配置爲 JSF 託管 bean。JSF 使用 Spring 的DelegatingVariableResolver
解析publicCatalog
和privateCatalog
bean。 - JSF 使用 Acegi 調用
publicCatalog
和privateCatalog
bean 的 getter 方法獲取公共和私有數據。 - Acegi 執行對訪問 bean 的授權過程。
- 如果 Acegi 發現用戶得到授權,可以訪問 bean,則調用 getter 方法獲取公共和私有數據,並將數據提供給 JSF。
- JSF 執行其生命週期並提供 JSF 頁面。
您可以看到,清單 7 的 JSF 頁面未使用任何託管 bean,所以 圖 2 不包含與 JSF 託管 bean 有關的事件。
本文的源代碼中還包含第二個示例應用程序,名爲 JSFAcegiSampleWithIOCBeans
(請參閱 下載)。 JSFAcegiSampleWithIOCBeans
使用 清單 7 中的 JSF 頁面演示了 IOC bean 在 JSF 頁面中的用法。
前一節演示了能夠直接在 JSF 應用程序中使用 IOC bean。如果已經有一個 JSF 應用程序,然後想用 Acegi 保護它,只需要執行以下四個配置步驟:
- 按照本系列的前三篇文章所描述的那樣編寫 Acegi 的配置文件。
- 按照 第 4 部分 中描述的那樣編寫一個 web.xml 文件。
- 按照本文的 “定義表達式解析器” 一節描述的那樣在 JSF 配置文件中使用 Spring 的
DelegatingVariableResolver
。 - 在 Acegi 的配置文件而不是 JSF 的配置文件中聲明 bean,重新配置要作爲 IOC bean 保護的 JSF 託管 bean。
使用這項技術,可以在不用考慮安全問題的情況下開發 JSF 應用程序。開發應用程序之後,可以按照以上四個配置步驟部署 Acegi,無需編寫任何 Java 安全性代碼。
在本系列的文章中,學習瞭如何使用 Acegi 保護 Java 應用程序。您現在掌握瞭如何加強基於 URL 的安全性和基於方法的安全性。還學習瞭如何設計訪問控制策略和在目錄服務其中託管這些策略,以及如何根據託管的訪問控制策略執行身份驗證和授權決策。在本文和前一篇文章中,主要關注了 JSF 應用程序,學習瞭如何在不編寫任何 Java 安全性代碼的情況下保護 JSF 應用程序。
描述 | 名字 | 大小 | 下載方法 |
---|---|---|---|
本文的示例代碼 | j-acegi5-source.zip | 42KB | HTTP |
來源:http://www.ibm.com/developerworks/cn/java/j-acegi5/