如何使用 Acegi 保护在 servlet 容器中运行的 JavaServer Faces (JSF) 应用程序。本文首先解释 Acegi 针对此目标提供的特性,并澄清一些关于使用 Acegi和JSF 的常见误解。然后提供一个简单的 web.xml 文件,可以用来部署 Acegi,从而保护 JSF应用程序。然后深入探讨 Acegi和JSF 组件,了解在部署 web.xml 文件和用户访问 JSF应用程序时所发生的事件。本文最后提供了一个由 Acegi 保护的示例 JSF应用程序。
无需编写 Java 代码即可添加安全性
回顾一下本系列的第一个示例 Acegi 应用程序(请参阅 第 1 部分 中的 “一个简单 Acegi 应用程序” 一节)。该应用程序使用 Acegi 提供了以下安全特性:
◆当一个未经验证的用户试图访问受保护的资源时,提供一个登录页面。
◆将授权用户直接重定向到所需的受保护资源。
◆如果用户未被授权访问受保护资源,提供一个访问拒绝页面。
回想一下,您无需编写任何 Java 代码就能获得这些特性。只需要对 Acegi 进行配置。同样,在 JSF应用程序中,无需编写任何 Java 代码,也应该能够从 Acegi 实现相同的特性。
澄清误解
其他一些作者似乎认为将 Acegi 与 JSF 集成需要 JSF应用程序提供登录页面(参见 参考资料)。这种观点并不正确。在需要时提供登录页面,这是 Acegi 的职责。确保登录页面在安全会话期间只出现一次,这也是 Acegi 的职责。然后,经过身份验证和授权的用户可以访问一个受保护资源,无需重复执行登录过程。
如果使用 JSF 提供登录页面,将会发生两个主要的问题:
◆当需要时,没有利用 Acegi 的功能提供登录页面。必须编写 Java 代码实现所有逻辑来提供登录页面。
◆至少需要编写一些 Java 代码将用户凭证(用户名和密码)从 JSF 的登录页面移交到 Acegi。
Acegi 的目的是避免编写 Java 安全代码。如果使用 JSF 提供登录页面,则没有实现这一用途,并且会引发一系列其他 JSF-Acegi 集成问题,所有这些问题都源于 “Acegi 是用来提供可配置安全性” 这一事实。如果试图使用 JSF 来完成 Acegi 的工作,将会遇到麻烦。
本文余下部分将解释并演示独立于 Acegi 的 JSF应用程序开发,并在稍后配置 Acegi 以保护 JSF应用程序 — 无需编写任何 Java 代码。首先看一下 web.xml 文件,可以部署该文件保护 JSF应用程序。
部署 Acegi 保护 JSF应用程序
清单 1 展示了一个 web.xml 文件(通常称为部署描述符),可以使用这个文件部署 Acegi,从而保护运行在 servlet 容器(比如 Apache Tomcat)中的 JSF应用程序:
清单 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 包含以下标记:
◆3 个 <context-param> 标记
◆2 个 <listener> 标记
◆1 个 <filter> 标记
◆1 个 <servlet> 标记
◆1 个 <servlet-mapping> 标记
◆1 个 <filter-mapping> 标记
阅读该文件,了解每个标记在 JSF-Acegi 应用程序中的用途。
向 Acegi和JSF 提供上下文参数
清单 1 中的每个
JSF 需要 javax.faces.STATE_SAVING_METHOD 和 javax.faces.CONFIG_FILES 参数。javax.faces.STATE_SAVING_METHOD 参数指定希望在客户机还是服务器上存储 JSF 页面-视图状态。Sun 的参考实现的默认行为是将 JSF 视图存储在服务器上。
javax.faces.CONFIG_FILES 参数指定 JSF 需要的配置文件的位置。JSF 配置文件的详细信息不属于本文讨论的范围(参见 参考资料,获取涉及该主题的资源链接)。
为 Acegi和JSF 配置侦听器
现在看一下 清单 1 中的 2 个
◆启动 JSP 或 servlet 应用程序时servlet容器创建一个新的 servlet 上下文。每当 JSP 或 servlet 应用程序启动时,就会触发此事件。
◆servlet 容器创建一个新的 servlet 请求对象。每当容器从客户机收到一个 HTTP 请求时,此事件就会发生。
◆建立一个新的 HTTP 会话。当请求客户机建立一个与 servlet 容器的会话时,此事件就会发生。
◆一个新属性被添加到 servlet 上下文、servlet 请求和 HTTP 会话对象。
◆servlet 上下文、servlet 请求或 HTTP 会话对象的一个现有属性被修改或删除。
例如,Spring Framework 实现一个 javax.servlet.ServletContextListener servlet 接口。实现此接口的 spring 类是 org.springframework.web.context.ContextLoaderListener。注意,这是 清单 1 的第一个
类似地,JSF 实现一个 com.sun.faces.config.ConfigureListener 类,该类实现一些事件-侦听接口。可以在 清单 1 的第二个
本文稍后将解释不同的事件-侦听器接口,以及 Acegi和JSF 事件-侦听器类内部执行的处理(请参阅 “启动 JSF-Acegi 应用程序” 和 “处理对受 Acegi 保护的 JSF 页面的请求”)。
配置和映射 servlet 过滤器
现在看一下 清单 1 中的
请注意 清单 1 中的
还需注意,清单 1 的
清单 1 中的
配置 JSF servlet
web.xml 文件中的
现在,您已经看到,web.xml 文件要部署 Acegi 以保护 JSF 应用程序所需的所有标记。您已经了解了侦听器、过滤器和 servlet 如何相互协作。从这里的讨论中可以看出,如果在 servlet 容器中部署 清单 1 中的 web.xml 文件,Acegi和JSF 都试图在两种情形下进行一些处理:
◆当启动应用程序时
◆当应用程序收到对 JSF 页面的请求时
【编辑推荐】