使用 Acegi 保护 Java 应用程序,第 5 部分: 保护 JSF 应用程序中的 JavaBean

这个 系列 由五部分组成,介绍了 Acegi Security System,并演示了如何使用 Acegi 保护企业级 Java 应用程序。本文是该系列的最后一部分,将继续讨论使用 Acegi 保护 JSF 应用程序。在 第 4 部分 中,我介绍了如何在不编写 Java 代码的情况下使用 Acegi 保护 JSF 页面。我还详细说明了部署 JSF-Acegi 应用程序和用户访问该程序时发生的事件。在本部分中,我将着重介绍在 JSF 应用程序中保护 JavaBean 的技术。

首先展示如何将 第 3 部分 中演示的 bean 安全性概念应用于 JSF 应用程序,其效果不是太理想。然后演示两项新技术,这些新技术特别适合在 JSF 应用程序中保护 JavaBean。最后,总结四点策略,可以让您不用编写任何 Java 代码就能够使用 Acegi 在 JSF 应用程序中保护 bean。

简单的技术

在 JSF 应用程序中使用安全 bean 的最简单方法就是,执行 第 3 部分 的清单 4 中介绍的五个步骤。在第 3 部分中,我从 servlet 上下文中取出了 Spring 框架的 Web 应用程序上下文对象。可以在以后使用 Web 应用程序上下文安全地访问 bean。下面的 清单 1 演示了如何在 JSF 页面中使用 Web 应用程序上下文:


清单 1. 从 servlet 上下文提取 Web 应用程序上下文,并将其用于 JSF 页面

 

可以看到,清单 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 页面

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

 

清单 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> 标签

 

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

清单 5 可以看出,Catalog 类只包含 publicDataprivateData 属性的 getter 和 setter 方法。JSF 框架将会调用 getter 和 setter 方法,我将在下一节解释这一点。

现在看一下两个 IOC bean(publicCatalogprivateCatalog)的实现,如 清单 6 所示:


清单 6. publicCatalogprivateCatalog IOC bean

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

清单 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 安全性代码。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值