保護 JSF 應用程序中的 JavaBean

簡單的技術

在 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 的兩個策略:

保護 JSF 託管 bean

請看 清單 2 所示的 JSF 頁面,其中使用了一個名爲 catalog 的 JSF 託管 bean:


清單 2. 使用託管 bean 的簡單 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="#{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 的 publicDataprivateData 屬性,它們分別提供公共和私有的編目數據。

第 3 部分 的 “訪問執行過代理的 Java 對象” 小節中,我配置了兩個 Acegi bean,分別名爲 publicCatalogprivateCatalog。現在我要將第 3 部分的 publicCatalog bean(不受保護的供公共訪問的 bean)映射到 catalog bean 的 publicData 屬性。類似的,將第 3 部分的 privateCatalog(在 第 3 部分 的清單 3 中配置的受保護且執行過代理的 bean)映射到上面 清單 2 的託管 bean catalogprivateData 屬性。映射完成後,catalog bean 就會充當 JSF 編目應用程序的公共和私有數據的包裝器。

定義託管 bean

清單 3 演示瞭如何定義 catalog bean,以便將它的 publicDataprivateData 屬性分別映射到 Acegi 的 publicCatalogprivateCatalog 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>。現在我要詳細解釋這兩個標籤。

在 faces 配置文件中聲明 bean 屬性

清單 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 配置文件不包含名爲 publicCatalogprivateCatalog 的託管 bean。我在 第 3 部分 的 “訪問執行過代理的 Java 對象” 小節中配置了 publicCatalogprivateCatalog 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 顯示了這個標籤,用於快速參考):


清單 4. <application> 標籤

                

<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 和 IOC bean

前面已經看到了如何配置 JSF 應用程序以使用 Acegi 的 IOC bean。現在可以看看剛剛配置的三個 bean。

清單 5 顯示了 Catalog 類的實現,它的實例 — 名爲 catalog — 被配置爲 JSF 中的託管 bean:


清單 5. Catalog

                
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 類只包含 publicDataprivateData 屬性的 getter 和 setter 方法。JSF 框架將會調用 getter 和 setter 方法,我將在下一節解釋這一點。

現在看一下兩個 IOC bean(publicCatalogprivateCatalog)的實現,如 清單 6 所示:


清單 6. publicCatalogprivateCatalog 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 如何協作使用這些組件和配置。

JSF 和 Acegi 協作保護託管 bean

當用戶試圖訪問 清單 2 的 JSF 頁面時,就會發生 圖 1 所示的一系列事件。我列出了支持 Acegi URL 安全性和 JSF 應用程序中的 bean 安全性的所有事件。


圖 1. JSF 和 Acegi 組件協作
圖 1. JSF 和 Acegi 組件協作

圖 1 所示的事件順序如下:

  1. 用戶訪問 JSF 頁面。

  2. Acegi 檢查該用戶是否有權訪問該 JSF 頁面。(請參閱 第 4 部分 的 “處理對受 Acegi 保護的 JSF 頁面的請求” 一節。)

  3. 如果授權過程成功,則將控制權轉到 faces servlet,由它準備提供 JSF 頁面。

  4. 在準備期間,JSF 找到 清單 2 所示的 JSF 頁面中的 catalog bean。

  5. JSF 檢查 清單 3 所示的配置文件,查找 catalog bean 的定義並將其實例化。JSF 還在配置文件中檢查 catalog bean 的屬性。它發現 catalog bean 的 publicDataprivateData 屬性被映射到 publicCatalogprivateCatalog bean,清單 3 中未將這兩個 bean 配置爲 JSF 託管 bean。

  6. JSF 使用 Spring 的 DelegatingVariableResolver 變量解析器(在 清單 4 中配置)解析 publicCatalogprivateCatalog bean。

  7. JSF 使用 Acegi 調用 publicCatalogprivateCatalog beans 的 getter 方法獲取公共和私有數據。

  8. Acegi 再次執行對訪問 bean 的授權過程。(請參閱 第 3 部分 對 Java 對象進行這一授權過程的詳細討論。)

  9. 如果 Acegi 發現用戶得到授權可以訪問 bean,就會調用 getter 方法,獲取公共和私有數據,並將數據提供給 JSF。

  10. JSF 調用 catalog bean 的 setter 方法在 catalog bean 中設置公共和私有數據。

  11. 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 頁面所示:


清單 7. 直接在 JSF 頁面中使用 IOC 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="#{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(即 publicCatalogprivateCatalog)。這意味着當用戶訪問 清單 7 的 JSF 頁面時,JSF 直接用 Spring 的 DelegatingVariableResolver 解析 Acegi IOC。

請注意,爲了解析 JSF 頁面中使用的 IOC bean, DelegatingVariableResolver 的工作方式與我在討論 圖 1 時說明的方式相同。

圖 2 演示了用戶訪問 清單 7 中的 JSF 頁面時發生的事件順序。


圖 2. JSF 和 Acegi 組件協作提供帶有安全 IOC bean 的 JSF 頁面
JSF 和 Acegi 組件協作提供帶有安全 IOC bean 的 JSF 頁面

圖 2 顯示的事件順序與 圖 1 的順序稍微有點不同:

  1. 用戶訪問 JSF 頁面。

  2. Acegi 檢查用戶是否有權訪問該 JSF 頁面。

  3. 如果授權過程成功,則將控制權轉移給 JSF,由 JSF 準備提供 JSF 頁面。

  4. 在準備期間,JSF 找到 清單 7 的 JSF 頁面中的 publicCatalogprivateCatalog bean。

  5. JSF 檢查 清單 3 的配置文件,發現 publicCatalogprivateCatalog bean 沒有在配置文件中配置爲 JSF 託管 bean。JSF 使用 Spring 的 DelegatingVariableResolver 解析 publicCatalogprivateCatalog bean。

  6. JSF 使用 Acegi 調用 publicCatalogprivateCatalog bean 的 getter 方法獲取公共和私有數據。

  7. Acegi 執行對訪問 bean 的授權過程。

  8. 如果 Acegi 發現用戶得到授權,可以訪問 bean,則調用 getter 方法獲取公共和私有數據,並將數據提供給 JSF。

  9. JSF 執行其生命週期並提供 JSF 頁面。

您可以看到,清單 7 的 JSF 頁面未使用任何託管 bean,所以 圖 2 不包含與 JSF 託管 bean 有關的事件。

本文的源代碼中還包含第二個示例應用程序,名爲 JSFAcegiSampleWithIOCBeans(請參閱 下載)。 JSFAcegiSampleWithIOCBeans 使用 清單 7 中的 JSF 頁面演示了 IOC bean 在 JSF 頁面中的用法。

使用 Acegi 保護現有 JSF 應用程序

前一節演示了能夠直接在 JSF 應用程序中使用 IOC bean。如果已經有一個 JSF 應用程序,然後想用 Acegi 保護它,只需要執行以下四個配置步驟:

  1. 按照本系列的前三篇文章所描述的那樣編寫 Acegi 的配置文件。
  2. 按照 第 4 部分 中描述的那樣編寫一個 web.xml 文件。
  3. 按照本文的 “定義表達式解析器” 一節描述的那樣在 JSF 配置文件中使用 Spring 的 DelegatingVariableResolver
  4. 在 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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章