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

本文探讨了如何使用Acegi安全框架保护JSF应用程序中的JavaBean,包括使用托管bean和IOC bean的方法,并提供了一个无需编写额外Java代码即可实现安全性的策略。

这个 系列 由五部分组成,介绍了 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 安全性代码。

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值