﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-狼爱上狸-随笔分类-Acegi</title><link>http://www.blogjava.net/waterjava/category/31364.html</link><description>我胡汉山又回来了!</description><language>zh-cn</language><lastBuildDate>Thu, 08 May 2008 12:53:09 GMT</lastBuildDate><pubDate>Thu, 08 May 2008 12:53:09 GMT</pubDate><ttl>60</ttl><item><title>acegi,IBM的Acegi Security System（4） </title><link>http://www.blogjava.net/waterjava/archive/2008/05/08/199314.html</link><dc:creator>狼爱上狸</dc:creator><author>狼爱上狸</author><pubDate>Thu, 08 May 2008 10:50:00 GMT</pubDate><guid>http://www.blogjava.net/waterjava/archive/2008/05/08/199314.html</guid><wfw:comment>http://www.blogjava.net/waterjava/comments/199314.html</wfw:comment><comments>http://www.blogjava.net/waterjava/archive/2008/05/08/199314.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/waterjava/comments/commentRss/199314.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/waterjava/services/trackbacks/199314.html</trackback:ping><description><![CDATA[<p>2008 年 3 月 20 日</p>
<blockquote>Bilal Siddiqui 将继续在他的 <a onmouseover="linkQueryAppend(this)" href="http://www.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=acegi" cmimpressionsent="1">系列文章</a> 中展示如何使用 Acegi 保护 Java&#8482;Server Faces (JSF) 应用程序。配置 JSF 和 Acegi，让它们在 servlet 容器中协作，探索 JSF 和 Acegi 组件如何彼此协作。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p>本 <a href="http://www.ibm.com/developerworks/cn/views/java/articles.jsp?view_by=search&amp;search_by=acegi" cmimpressionsent="1">系列</a> 的前 3 部分讨论了如何使用 Acegi Security System 保护 Java 企业应用程序：</p>
<ul>
    <li><a onmouseover="linkQueryAppend(this)" href="http://www.ibm.com/developerworks/cn/java/j-acegi1/" cmimpressionsent="1">第 1 部分</a> 解释了如何使用 Acegi 的内置过滤器实现一个简单的基于 URL 的安全系统。
    <li><a onmouseover="linkQueryAppend(this)" href="http://www.ibm.com/developerworks/cn/java/j-acegi2/" cmimpressionsent="1">第 2 部分</a> 展示了如何编写访问控制策略、将其存储在 LDAP 目录服务器中，以及配置 Acegi 与 LDAP 服务器交互，从而实现访问控制策略。
    <li><a onmouseover="linkQueryAppend(this)" href="http://www.ibm.com/developerworks/cn/java/j-acegi3/" cmimpressionsent="1">第 3 部分</a> 展示了如何在企业应用程序中使用 Acegi 保护对 Java 类实例的访问。 </li>
</ul>
<p>第 4 部分将讨论如何使用 Acegi 保护在 servlet 容器中运行的 JavaServer Faces (JSF) 应用程序。本文首先解释 Acegi 针对此目标提供的特性，并澄清一些关于使用 Acegi 和 JSF 的常见误解。然后提供一个简单的 web.xml 文件，可以用来部署 Acegi，从而保护 JSF 应用程序。然后深入探讨 Acegi 和 JSF 组件，了解在部署 web.xml 文件和用户访问 JSF 应用程序时所发生的事件。本文最后提供了一个由 Acegi 保护的示例 JSF 应用程序。 </p>
<p><a name="section1"><span class="atitle">无需编写 Java 代码即可添加安全性</span></a></p>
<p>回顾一下本系列的第一个示例 Acegi 应用程序（请参阅 <a onmouseover="linkQueryAppend(this)" href="http://www.ibm.com/developerworks/cn/java/j-acegi1/" cmimpressionsent="1">第 1 部分</a> 中的 &#8220;一个简单 Acegi 应用程序&#8221; 一节）。该应用程序使用 Acegi 提供了以下安全特性：</p>
<ul>
    <li>当一个未经验证的用户试图访问受保护的资源时，提供一个登录页面。
    <li>将授权用户直接重定向到所需的受保护资源。
    <li>如果用户未被授权访问受保护资源，提供一个访问拒绝页面。 </li>
</ul>
<p>回想一下，您无需编写任何 Java 代码就能获得这些特性。只需要对 Acegi 进行配置。同样，在 JSF 应用程序中，无需编写任何 Java 代码，也应该能够从 Acegi 实现相同的特性。</p>
<p><a name="N100B2"><span class="smalltitle">澄清误解</span></a></p>
<p>其他一些作者似乎认为将 Acegi 与 JSF 集成需要 JSF 应用程序提供登录页面（参见 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#resources" cmimpressionsent="1">参考资料</a>）。这种观点并不正确。在需要时提供登录页面，这是 Acegi 的职责。确保登录页面在安全会话期间只出现一次，这也是 Acegi 的职责。然后，经过身份验证和授权的用户可以访问一个受保护资源，无需重复执行登录过程。</p>
<p>如果使用 JSF 提供登录页面，将会发生两个主要的问题：</p>
<ul>
    <li>当需要时，没有利用 Acegi 的功能提供登录页面。必须编写 Java 代码实现所有逻辑来提供登录页面。<br />
    <br />
    <li>至少需要编写一些 Java 代码将用户凭证（用户名和密码）从 JSF 的登录页面移交到 Acegi。 </li>
</ul>
<p>Acegi 的目的是避免编写 Java 安全代码。如果使用 JSF 提供登录页面，则没有实现这一用途，并且会引发一系列其他 JSF-Acegi 集成问题，所有这些问题都源于 &#8220;Acegi 是用来提供可配置安全性&#8221; 这一事实。如果试图使用 JSF 来完成 Acegi 的工作，将会遇到麻烦。</p>
<p>本文余下部分将解释并演示独立于 Acegi 的 JSF 应用程序开发，并在稍后配置 Acegi 以保护 JSF 应用程序 — 无需编写任何 Java 代码。首先看一下 web.xml 文件，可以部署该文件保护 JSF 应用程序。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section2"><span class="atitle">部署 Acegi 保护 JSF 应用程序</span></a></p>
<p><a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#listing1" cmimpressionsent="1">清单 1</a> 展示了一个 web.xml 文件（通常称为<em>部署描述符</em>），可以使用这个文件部署 Acegi，从而保护运行在 servlet 容器（比如 Apache Tomcat）中的 JSF 应用程序：</p>
<br />
<a name="listing1"><strong>清单 1. 用于部署 Acegi 和 servlet 容器中的 JSF 的 web.xml 文件</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;?xml version="1.0"?&gt;
            &lt;!DOCTYPE web-app PUBLIC
            "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
            "http://java.sun.com/dtd/web-app_2_3.dtd"&gt;
            &lt;web-app&gt;
            &lt;context-param&gt;
            &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
            &lt;param-value&gt;/WEB-INF/acegi-config.xml&lt;/param-value&gt;
            &lt;/context-param&gt;
            &lt;context-param&gt;
            &lt;param-name&gt;javax.faces.STATE_SAVING_METHOD&lt;/param-name&gt;
            &lt;param-value&gt;server&lt;/param-value&gt;
            &lt;/context-param&gt;
            &lt;context-param&gt;
            &lt;param-name&gt;javax.faces.CONFIG_FILES&lt;/param-name&gt;
            &lt;param-value&gt;/WEB-INF/faces-config.xml&lt;/param-value&gt;
            &lt;/context-param&gt;
            &lt;listener&gt;
            &lt;listener-class&gt;
            org.springframework.web.context.ContextLoaderListener
            &lt;/listener-class&gt;
            &lt;/listener&gt;
            &lt;listener&gt;
            &lt;listener-class&gt;
            com.sun.faces.config.ConfigureListener
            &lt;/listener-class&gt;
            &lt;/listener&gt;
            &lt;!-- Faces Servlet --&gt;
            &lt;servlet&gt;
            &lt;servlet-name&gt;Faces Servlet&lt;/servlet-name&gt;
            &lt;servlet-class&gt;javax.faces.webapp.FacesServlet&lt;/servlet-class&gt;
            &lt;load-on-startup&gt; 1 &lt;/load-on-startup&gt;
            &lt;/servlet&gt;
            &lt;!-- Faces Servlet Mapping --&gt;
            &lt;servlet-mapping&gt;
            &lt;servlet-name&gt;Faces Servlet&lt;/servlet-name&gt;
            &lt;url-pattern&gt;*.faces&lt;/url-pattern&gt;
            &lt;/servlet-mapping&gt;
            &lt;!-- Acegi filter configuration --&gt;
            &lt;filter&gt;
            &lt;filter-name&gt;Acegi Filter Chain Proxy&lt;/filter-name&gt;
            &lt;filter-class&gt;
            org.acegisecurity.util.FilterToBeanProxy
            &lt;/filter-class&gt;
            &lt;init-param&gt;
            &lt;param-name&gt;targetClass&lt;/param-name&gt;
            &lt;param-value&gt;
            org.acegisecurity.util.FilterChainProxy
            &lt;/param-value&gt;
            &lt;/init-param&gt;
            &lt;/filter&gt;
            &lt;!-- Acegi Filter Mapping --&gt;
            &lt;filter-mapping&gt;
            &lt;filter-name&gt;Acegi Filter Chain Proxy&lt;/filter-name&gt;
            &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
            &lt;/filter-mapping&gt;
            &lt;/web-app&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>注意，<a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#listing1" cmimpressionsent="1">清单 1</a> 包含以下标记：</p>
<ul>
    <li>3 个 <code>&lt;context-param&gt;</code> 标记
    <li>2 个 <code>&lt;listener&gt;</code> 标记
    <li>1 个 <code>&lt;filter&gt;</code> 标记
    <li>1 个 <code>&lt;servlet&gt;</code> 标记
    <li>1 个 <code>&lt;servlet-mapping&gt;</code> 标记
    <li>1 个 <code>&lt;filter-mapping&gt;</code> 标记 </li>
</ul>
<p>阅读该文件，了解每个标记在 JSF-Acegi 应用程序中的用途。</p>
<p><a name="N10128"><span class="smalltitle">向 Acegi 和 JSF 提供上下文参数</span></a></p>
<p><a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#listing1" cmimpressionsent="1">清单 1</a> 中的每个 <code>&lt;context-param&gt;</code> 标记定义一个参数，供 Acegi 或 JSF 在启动或执行期间使用。第一个参数 — <code>contextConfigLocation</code> — 定义 Acegi 的 XML 配置文件的位置。</p>
<p>JSF 需要 <code>javax.faces.STATE_SAVING_METHOD</code> 和 <code>javax.faces.CONFIG_FILES</code> 参数。<code>javax.faces.STATE_SAVING_METHOD</code> 参数指定希望在客户机还是服务器上存储 JSF 页面-视图状态。Sun 的参考实现的默认行为是将 JSF 视图存储在服务器上。</p>
<p><code>javax.faces.CONFIG_FILES</code> 参数指定 JSF 需要的配置文件的位置。JSF 配置文件的详细信息不属于本文讨论的范围（参见 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#resources" cmimpressionsent="1">参考资料</a>，获取涉及该主题的资源链接）。</p>
<p><a name="N1015B"><span class="smalltitle">为 Acegi 和 JSF 配置侦听器</span></a></p>
<p>现在看一下 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#listing1" cmimpressionsent="1">清单 1</a> 中的 2 个 <code>&lt;listener&gt;</code> 标记。<code>&lt;listener&gt;</code> 标记定义侦听器类，侦听器类侦听并处理 JSP 或 servlet 应用程序启动和执行期间发生的事件。例如：</p>
<ul>
    <li>启动 JSP 或 servlet 应用程序时，servlet 容器创建一个新的 servlet 上下文。每当 JSP 或 servlet 应用程序启动时，就会触发此事件。<br />
    <br />
    <li>servlet 容器创建一个新的 servlet 请求对象。每当容器从客户机收到一个 HTTP 请求时，此事件就会发生。 <br />
    <br />
    <li>建立一个新的 HTTP 会话。当请求客户机建立一个与 servlet 容器的会话时，此事件就会发生。 <br />
    <br />
    <li>一个新属性被添加到 servlet 上下文、servlet 请求和 HTTP 会话对象。<br />
    <br />
    <li>servlet 上下文、servlet 请求或 HTTP 会话对象的一个现有属性被修改或删除。 </li>
</ul>
<p><code>&lt;listener&gt;</code> 标记就像一种可扩展性机制，允许在 servlet 容器内部运行的应用程序协同某些事件进行处理。servlet 规范定义了侦听器类为处理事件而实现的一些接口。</p>
<p>例如，Spring Framework 实现一个 <code>javax.servlet.ServletContextListener</code> servlet 接口。实现此接口的 spring 类是 <code>org.springframework.web.context.ContextLoaderListener</code>。注意，这是 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#listing1" cmimpressionsent="1">清单 1</a> 的第一个 <code>&lt;listener&gt;</code> 标记中的侦听器类。 </p>
<p>类似地，JSF 实现一个 <code>com.sun.faces.config.ConfigureListener</code> 类，该类实现一些事件-侦听接口。可以在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#listing1" cmimpressionsent="1">清单 1</a> 的第二个 <code>&lt;listener&gt;</code> 标记中找到 <code>ConfigureListener</code> 类。</p>
<p>本文稍后将解释不同的事件-侦听器接口，以及 Acegi 和 JSF 事件-侦听器类内部执行的处理（请参阅 &#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#section4" cmimpressionsent="1">启动 JSF-Acegi 应用程序</a>&#8221; 和 &#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#section5" cmimpressionsent="1">处理对受 Acegi 保护的 JSF 页面的请求</a>&#8221;）。</p>
<p><a name="N101CA"><span class="smalltitle">配置和映射 servlet 过滤器</span></a></p>
<p>现在看一下 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#listing1" cmimpressionsent="1">清单 1</a> 中的 <code>&lt;filter&gt;</code> 标记。在请求的 servlet 处理传入的请求之前，servlet 应用程序使用过滤器对其进行预处理。在请求执行之前，Acegi 使用 servlet 过滤器对用户进行身份验证。</p>
<p>请注意 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#listing1" cmimpressionsent="1">清单 1</a> 中的 <code>&lt;filter&gt;</code> 标记，它的 <code>&lt;filter-class&gt;</code> 子标记指定一个 <code>org.acegisecurity.util.FilterToBeanProxy</code> 类。<code>FilterToBeanProxy</code> 类是 Acegi 的一部分。此类实现一个 <code>javax.servlet.Filter</code> 接口，该接口是 servlet 应用程序的一部分。<code>javax.servlet.Filter</code> 接口有一个 <code>doFilter()</code> 方法，servlet 容器在收到请求时调用该方法。</p>
<p>还需注意，<a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#listing1" cmimpressionsent="1">清单 1</a> 的 <code>&lt;filter&gt;</code> 标记有另一个子标记 <code>&lt;init-param&gt;</code>。<code>&lt;init-param&gt;</code> 标记指定实例化 <code>FilterToBeanProxy</code> 类所需的参数。可以从 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#listing1" cmimpressionsent="1">清单 1</a> 中看出，<code>FilterToBeanProxy</code> 类只需要一个参数，该参数是 <code>FilterChainProxy</code> 类的一个对象。<code>FilterChainProxy</code> 类表示 <a onmouseover="linkQueryAppend(this)" href="http://www.ibm.com/developerworks/cn/java/j-acegi1" cmimpressionsent="1">第 1 部分 1</a> 中讨论的整个 Acegi 过滤器链（请参阅 &#8220;安全过滤器&#8221; 小节）。<code>FilterToBeanProxy</code> 类的 <code>doFilter()</code> 方法使用 <code>FilterChainProxy</code> 类执行 Acegi 的安全过滤器链。</p>
<p><a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#listing1" cmimpressionsent="1">清单 1</a> 中的 <code>&lt;filter-mapping&gt;</code> 标记指定调用 Acegi 的 <code>FilterToBeanProxy</code> 的请求 URL。我已经将所有的 JSF 页面映射到 Acegi 的 <code>FilterToBeanProxy</code>。这意味着只要用户试图访问 JSF 页面，<code>FilterChainProxy</code> <code>doFilter()</code> 方法就会自动获得控制权。</p>
<p><a name="N10251"><span class="smalltitle">配置 JSF servlet</span></a></p>
<p>web.xml 文件中的 <code>&lt;servlet&gt;</code> 标记指定希望从特定 URl 调用的 servlet（在本例中是一个 JSF servlet）。<code>&lt;servlet-mapping&gt;</code> 标记定义该 URL。几乎所有的 JSP 或 servlet 应用程序都包含这两个标记，所以无需再作讨论（参见 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#resources" cmimpressionsent="1">参考资料</a>，获取讨论 servlet 编程的资源链接）。</p>
<p>现在，您已经看到，web.xml 文件要部署 Acegi 以保护 JSF 应用程序所需的所有标记。您已经了解了侦听器、过滤器和 servlet 如何相互协作。从这里的讨论中可以看出，如果在 servlet 容器中部署 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#listing1" cmimpressionsent="1">清单 1</a> 中的 web.xml 文件，Acegi 和 JSF 都试图在两种情形下进行一些处理：</p>
<ul>
    <li>当启动应用程序时
    <li>当应用程序收到对 JSF 页面的请求时 </li>
</ul>
<p>接下来的两节解释每种情况中发生的一系列事件。 </p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section4"><span class="atitle">启动 JSF-Acegi 应用程序</span></a></p>
<p><a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#fig1" cmimpressionsent="1">图 1</a> 展示了在 JSF-Acegi 应用程序启动时发生的事件顺序：</p>
<br />
<a name="fig1"><strong>图 1. JSF-Acegi 应用程序启动时发生的事件顺序</strong></a><br />
<img height="261" alt="JSF-Acegi 应用程序启动时发生的事件顺序" src="http://www.ibm.com/developerworks/cn/java/j-acegi4/Figure1.gif" width="459" /> <br />
<br />
<br />
<p>详细来讲，<a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#fig1" cmimpressionsent="1">图 1</a> 显示的事件顺序如下所示：</p>
<ol>
    <li>servlet 容器实例化在 web.xml 文件中配置的所有侦听器。<br />
    <br />
    <li>servlet 容器将 Acegi 的 <code>ContextLoaderListener</code> 注册为一个侦听器类，该类实现 <code>javax.servlet.ServletContextListener</code>接口。<code>ServletContextListener</code> 接口包含两个重要方法：<code>contextInitialized()</code> 和 <code>contextDestroyed()</code>：
    <ul>
        <li><code>contextInitialized()</code> 方法在初始化 servlet 上下文时获得控制权。
        <li>类似地，当应用程序退出时，<code>contextDestroyed()</code> 方法会被调用，并消除 servlet 上下文。 </li>
    </ul>
    <br />
    <li>servlet 容器将 JSF 的 <code>ConfigureListener</code> 注册为另一个侦听器。JSF 的 <code>ConfigureListener</code> 实现许多侦听器接口，比如 <code>ServletContextListener</code>、 <code>ServletContextAttributeListener</code>、 <code>ServletRequestListener</code>，以及 <code>ServletRequestAttributeListener</code>。您已经看到了 <code>ServletContextListener</code> 接口的方法。余下的接口是：
    <ul>
        <li><code>ServletContextAttributeListener</code>，它包含 3 种方法：<code>attributeAdded()</code> <code>attributeRemoved()</code> 和 <code>attributeReplaced()</code>。这 3 种方法分别在某个属性被添加到 servlet 上下文、被从 servlet 上下文删除、被新属性取代时获得控制权。<code>attributeReplaced()</code> 方法在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#section5" cmimpressionsent="1">处理对受 Acegi 保护的 JSF 页面的请求</a> 小节的第 8 步中获得控制权。<br />
        <br />
        <li><code>ServletRequestListener</code> 中包含的方法在创建或删除新的 servlet 请求对象时获得控制权。servlet 请求方法表示并包装来自用户的请求。<br />
        <br />
        <li><code>ServletRequestAttributeListener</code> 中包含的方法在添加、删除或替换某个请求对象的属性时获得控制权。本文稍后将讨论在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#section5" cmimpressionsent="1">处理对受 Acegi 保护的 JSF 页面的请求</a> 小节的第 3 步中创建一个新的请求对象时，JSF 的 <code>ConfigureListener</code> 执行的处理。 </li>
    </ul>
    <br />
    <li>servlet 容器创建一个 <em>servlet 上下文对象</em>，该对象封装应用程序资源（比如 JSP 页面、Java 类和应用程序初始化参数），并允许整个应用程序访问这些资源。JSF-Acegi 应用程序的所有其他组件（侦听器、过滤器，以及 servlet）在 servlet 上下文对象中以属性的形式存储与应用程序资源相关的信息。<br />
    <br />
    <li>servlet 容器通知 Acegi 的 <code>ContextLoaderListener</code>，servlet 上下文是通过调用 <code>ContextLoaderListener</code> 的 <code>contextInitializated()</code> 方法初始化的。<br />
    <br />
    <li><code>contextInitialized()</code> 方法解析 Acegi 的配置文件，为 JSF-Acegi 应用程序创建 Web 应用程序上下文，以及实例化所有的安全过滤器和在 Acegi 配置文件中配置的 Jave bean。在以后 JSF 应用程序收到来自客户机的请求时，这些过滤器对象将会用于身份验证和授权（参阅 <a onmouseover="linkQueryAppend(this)" href="http://www.ibm.com/developerworks/cn/java/j-acegi3/" cmimpressionsent="1">第 3 部分</a> 中关于 Web 应用程序上下文创建的讨论和图 1）。 <br />
    <br />
    <li>servlet 容器通知 JSF 的 <code>ConfigureListener</code>，servlet 上下文是通过调用 <code>contextInitialized()</code> 方法初始化的。<br />
    <br />
    <li><code>contextInitialized()</code> 方法检查在 JSF 配置文件中配置的所有 JSF 托管 bean，确保 Java 类与每个 bean 并存。<br />
    <br />
    <li>servlet 容器检查 web.xml 文件中任何配置的过滤器。例如，<a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#listing1" cmimpressionsent="1">清单 1</a> 中的 web.xml 文件包含一个 Acegi 过滤器 <code>FilterToBeanProxy</code>，servlet 容器将其实例化、初始化并注册为一个过滤器。Acegi 现在可以对传入的请求执行身份验证和授权了。 <br />
    <br />
    <li>servlet 容器实例化 faces servlet，后者开始侦听从用户传入的请求。 </li>
</ol>
<p>下一节解释 JSF-Acegi 应用程序收到来自用户的请求时发生的一系列事件。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section5"><span class="atitle">处理对受 Acegi 保护的 JSF 页面的请求</span></a></p>
<p>您已经了解了如何配置 Acegi 保护 JSF 应用程序。也看到了当启动 JSF-Acegi 应用程序时发生的一系列事件。本节描述当用户发送一个对受 Acegi 保护的 JSF 页面的请求时，JSF 和 Acegi 组件如何在 servlet 容器的框架中运行。</p>
<p><a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#fig2" cmimpressionsent="1">图 2</a> 展示了当客户机发送一个对受 Acegi 保护的 JSF 页面的请求时，发生的事件顺序：</p>
<br />
<a name="fig2"><strong>图 2. JSF 和 Acegi 协作提供 JSF 页面</strong></a><br />
<img height="261" alt="JSF 和 Acegi 协作提供 JSF 页面" src="http://www.ibm.com/developerworks/cn/java/j-acegi4/Figure2.gif" width="459" /> <br />
<br />
<br />
<p>详细来讲，<a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#fig2" cmimpressionsent="1">图 2</a> 展示的事件顺序如下所示：</p>
<ol>
    <li>servlet 容器创建一个表示用户请求的 servlet 请求对象。<br />
    <br />
    <li>回想一下 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#section4" cmimpressionsent="1">启动 JSF-Acegi 应用程序</a> 小节中的第 3 步，JSF 的 <code>ConfigureListener</code> 实现 <code>ServletRequestListener</code> 接口。这意味着 <code>ConfigureListener</code> 侦听与创建和删除 servlet 请求对象相关的事件。因此，servlet 容器调用 <code>ConfigureListener</code> 类的 <code>requestInitialized()</code> 方法。<br />
    <br />
    <li><code>requestInitialized()</code> 方法准备执行请求的 JSF 生命周期。准备过程包括检查请求的 faces 上下文是否存在。faces 上下文封装与应用程序资源相关的信息。faces servlet 执行 JSF 生命周期时需要这些信息。如果此请求是新会话的第一个请求，就会缺少 faces 上下文。在这种情况下，<code>requestInitialized()</code> 方法创建一个新的 faces 上下文。<br />
    <br />
    <li>servlet 容器检查用户的请求是否带有任何状态信息。如果 servlet 容器未找到状态信息，它会假设该请求是新会话的第一个请求，并为用户创建一个 HTTP 会话对象。如果 servlet 容器发现该请求包含某种状态信息（比如一个 cookie 或 URL 中的某种状态信息），它就会根据保存的会话信息恢复用户以前的会话。<br />
    <br />
    <li>servlet 容器把请求 URL 与一个 URL 模式进行匹配，这个 URL 模式包含在配置描述符中的 <code>&lt;filter-mapping&gt;</code> 标记的 <code>&lt;url-pattern&gt;</code> 子标记中。如果请求 URL 与这个 URL 模式匹配，servlet 容器调用 Acegi 的 <code>FilterToBeanProxy</code>，<code>FilterToBeanProxy</code> 已在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#fig1" cmimpressionsent="1">图 1</a> 的第 9 步中被注册为一个 servlet 过滤器。<br />
    <br />
    <li>Acegi 的 <code>FilterToBeanProxy</code> 使用 <code>FilterChainProxy</code> 类执行 Acegi 的完整的安全过滤器链。Acegi 的过滤器自动检查第 4 步中创建的 HTTP 会话对象，以查看请求客户机是否已被验证。如果 Acegi 发现用户未被验证，它提供一个登录页面。否则，它就直接执行 <a onmouseover="linkQueryAppend(this)" href="http://www.ibm.com/developerworks/cn/java/j-acegi2/" cmimpressionsent="1">第 2 部分</a> 的 &#8220;配置拦截器&#8221; 一节中描述的授权过程。 <br />
    <br />
    <li>Acegi 使用经过验证的用户的会话信息更新 servlet 上下文。<br />
    <br />
    <li>servlet 容器通知 JSF 的 <code>ConfigureListener</code> 的 <code>attributeReplaced()</code> 方法，servlet 上下文已被更新。<code>ConfigureListener</code> 检查是否有任何 JSF bean 被更改。如果发现任何更改，它相应地更新 faces 上下文。但是，在本例中，在身份验证过程中 Acegi 没有更改任何 JSF 托管 bean，因此在此调用期间 <code>ConfigureListener</code> 不进行任何处理。<br />
    <br />
    <li>如果授权过程成功，控制权被转移到 faces servlet，它执行 JSF 生命周期并向用户发回一个响应。 </li>
</ol>
<p>现在，您了解了 JSF 和 Acegi 如何协作提供 JSF 请求，接下来看一下完成后的 JSF 和 Acegi。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section6"><span class="atitle">示例 JSF-Acegi 应用程序</span></a></p>
<p>本文的下载部分（参见 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#download" cmimpressionsent="1">下载</a>）包含一个示例 JSF-Acegi 应用程序 JSFAcegiSample，演示了 Acegi 与 JSF 的简单集成。示例应用程序使用 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#listing1" cmimpressionsent="1">清单 1</a> 中的 web.xml。 </p>
<p>要部署示例应用程序，执行 <a onmouseover="linkQueryAppend(this)" href="http://www.ibm.com/developerworks/cn/java/j-acegi1/" cmimpressionsent="1">第 1 部分</a> 的 &#8220;部署并运行应用程序&#8221; 一节中的两个步骤。还需要从 Sun 的 JSF 站点（参见 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi4/index.html?S_TACT=105AGX52&amp;S_CMP=techcsdn#resources" cmimpressionsent="1">参考资料</a>）下载并解压 jsf-1_1_01.zip。将 jsf-1.1.X.zip 中的所有文件复制到 JSFAcegiSample 应用程序的 WEB-INF/lib 文件夹中。</p>
<p>从浏览器访问 http://localhost:8080/JSFAcegiSample，可以调用示例应用程序。JSFAcegiSample 应用程序显示一个索引页面和一个登录页面，索引页面中包含受保护资源的链接。所有受保护页面都是使用 JSF 组件开发的，而 Acegi 提供登录页面并执行身份验证和授权。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section7"><span class="atitle">结束语</span></a></p>
<p>在本文中，了解了如何配置 Acegi 以保护 JSF 应用程序。还详细了解了 JSF 和 Acegi 组件如何在一个 servlet 容器的框架中协作。最后，尝试运行了一个示例 JSF-Acegi 应用程序。</p>
<p>关于实现 JSF 应用程序的 Acegi 安全性，还涉及到更多内容。本系列的下一篇文章将演示如何使用 Acegi 保护对 JSF 的托管 bean 的访问。<br />
<br />
<br />
来自: http://www.cnblogs.com/amboyna/archive/2008/03/25/1122089.html</p>
<img src ="http://www.blogjava.net/waterjava/aggbug/199314.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/waterjava/" target="_blank">狼爱上狸</a> 2008-05-08 18:50 <a href="http://www.blogjava.net/waterjava/archive/2008/05/08/199314.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>acegi,IBM的Acegi Security System（3） </title><link>http://www.blogjava.net/waterjava/archive/2008/05/08/199312.html</link><dc:creator>狼爱上狸</dc:creator><author>狼爱上狸</author><pubDate>Thu, 08 May 2008 10:47:00 GMT</pubDate><guid>http://www.blogjava.net/waterjava/archive/2008/05/08/199312.html</guid><wfw:comment>http://www.blogjava.net/waterjava/comments/199312.html</wfw:comment><comments>http://www.blogjava.net/waterjava/archive/2008/05/08/199312.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/waterjava/comments/commentRss/199312.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/waterjava/services/trackbacks/199312.html</trackback:ping><description><![CDATA[<p>2007 年 10 月 18 日</p>
<blockquote>本文是 Acegi Security Systerm 介绍的最后一部分（共三部分），Bilal Siddiqui 将向您介绍如何保护对 Java 类实例的访问，从而结束本系列文章。通过本文了解为何需要对 Java&#8482; 类的访问进行保护，Spring 如何创建和保护对 Java 类实例的访问以及如何对 Acegi 进行配置以实现 Java 应用程序的类安全性。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p>这期共分三部分的系列文章介绍了如何使用 Acegi 安全系统保护 Java 企业应用程序。系列文章的 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html" cmimpressionsent="1">第 1 部分</a> 简单介绍了 Acegi 并解释如何使用其内置的安全过滤器实现一个简单的、基于 URL 的安全系统。<a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/" cmimpressionsent="1">第 2 部分</a> 介绍了如何编写访问控制策略并将其保存到一个 LDAP 目录服务器，以及如何配置 Acegi 来与目录服务器进行交互，从而实现访问控制策略。第 3 部分（也是本系列的最后一篇文章）将演示如何在企业应用程序中使用 Acegi 保护对 Java 类实例的访问。 </p>
<p>首先我将介绍何时需要对 Java 类访问进行保护，包括文中引用的两个典型企业应用程序场景。之后，我将解释 Spring 的反转控制（IOC）框架如何创建可从 JSP 或 servlet 访问的 Java 类实例。我还将介绍有关 <em>bean 代理</em> 的重要概念，Spring 正是使用它过滤对 Java 类的访问。最后，我将介绍如何对 Acegi 的方法安全性拦截器进行配置以控制对 Java 类的访问。我将对 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2" cmimpressionsent="1">第 2 部分</a> 中的示例程序进行增强，为实现安全的 Java 对象提供支持，从而结束本系列的最后一篇文章。 </p>
<p>由于本文的讨论构建在本系列前两部分的内容之上，因此会经常引用到 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html" cmimpressionsent="1">第 1 部分</a> 和 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/index.html" cmimpressionsent="1">第 2 部分</a> 中的讨论和示例。因此，在继续阅读本文之前，在其他浏览器窗口中打开前两期文章将有助于理解本文内容。</p>
<p><a name="section1"><span class="atitle">保护 Java 类的用例</span></a></p>
<p>您可能还记得，我曾在本系列的开头部分简单介绍了 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html#section1" cmimpressionsent="1">企业应用程序安全性</a>。在那次讨论中我曾提到过一种场景，其中 URL 安全性并不能完全满足这种场景的安全需求：</p>
<blockquote>假设有这样一个 PDF 文档，其中包含了某制造业公司生产的特定产品的数据。文档的一部分包含了设计数据，将由公司设计部分进行编辑和更新。文档另一部分包含生产经理将使用到的生产数据。对于此类场景，需要实现更加细粒度的安全性，对文档的不同部分应用不同的访问权限。</blockquote>
<p>在继续阅读之前，请考虑更多的应用程序场景，除了实现 URL 安全性以外，这些场景还要求您对单独的类访问进行保护。</p>
<p><a name="section1.1"><span class="smalltitle">业务自动化</span></a></p>
<p>业务自动化应用程序中的工作流由多个流程组成。例如，病理学实验室中执行血液测试的工作流由若干个步骤组成，其中每个步骤可看作一个业务流程：</p>
<ol>
    <li>工作人员从病人处采集血液样本并为其分配一个 ID。
    <li>实验室技术人员对样本进行必要的测试并准备测试结果。
    <li>由具备相应资格的病理学专家根据测试结果编写测试报告。 </li>
</ol>
<p>很明显，每个流程分别由单独的授权用户执行。未授权的用户则无权执行流程。例如，实验室研究人员只负责准备试验结果，而无权编写测试报告。</p>
<p>几乎所有的业务自动化应用程序都普遍使用授权的业务流程。通常，每个业务流程被实现为一个 Java 类，并且需要使用合适的访问控制策略对所有类实施保护。</p>
<p><a name="section1.2"><span class="smalltitle">企业对企业（Business-to-business）集成</span></a></p>
<p>Business-to-business (B2B) 集成指一种常见的场景，其中的两个企业实体需要彼此公开各自的特定功能。例如，宾馆可能向旅游公司公开其房间预订功能，而后者使用该功能为游客预订空闲的房间。作为合作伙伴的旅游公司可能具有一个特定的订房率。在这个场景中，宾馆的订房系统必须先对旅游公司进行身份验证，然后才能允许他们访问所选择的类，以便按照特定的订房率进行房间预订。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section2"><span class="atitle">使用 Spring 创建 Java 对象</span></a></p>
<p>现在您已经了解了对 Java 类示例的访问进行保护的重要性。在介绍能够实现更高级安全性的 Acegi 新功能之前，我将引导您回顾 Spring 框架的几个关键特性，您需要了解这些内容才能继续后文的示例。</p>
<p>首先对一些 Java 类进行配置并执行实例化。<a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html#section3" cmimpressionsent="1">第 1 部分</a> 曾介绍过，Java 类在 Spring 的 XML 配置文件中进行配置。在 Spring 配置文件中配置 Java 类的过程与 Acegi 过滤器的配置过程完全相同，因此这里不多做介绍。相反，我们将查看清单 1，它展示了名为 <code>publicCatalog</code> 的 bean 的配置：</p>
<br />
<a name="listing1"><strong>清单 1. Acegi XML 配置文件</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            &lt;beans&gt;
            &lt;bean id="publicCatalog"
            class="com.catalog.PublicCatalog" /&gt;
            &lt;!--Other bean tags --&gt;
            &lt;beans&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>了解 Spring 的 IOC 框架如何从 XML 配置文件读取 Java 类信息以及如何进行实例化，这一点非常重要。您可能还记得，我在系列文章的 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html" cmimpressionsent="1">第 1 部分</a> 中使用一个 web.xml 文件配置 <code>&lt;listener&gt;</code> 标记，它指向名为 <code>ContextLoaderListener</code> 的类。<code>ContextLoaderListener</code> 装载 Spring 的 IOC 框架并创建 Java 对象。您可以参考 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html#section6.1" cmimpressionsent="1">第 1 部分的清单 8</a> 查看全部内容。图 1 也对此进行了描述：</p>
<br />
<a name="figure1"><strong>图 1. 装载 Spring 的 IOC 框架并创建 Java 对象</strong></a><br />
<img height="236" alt="装载 Spring 的 IOC 框架并创建 Java 对象的步骤" src="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/Figure1.jpg" width="452" /> <br />
<p>现在我们将详细讨论这些步骤：</p>
<ol>
    <li>当初始化 Acegi 应用程序时，servlet 容器（本例中为 Apache Tomcat）创建了一个 <em>servlet 上下文</em>，其中保存了有关应用程序资源的信息，例如 JSP 页面和类。<br />
    <br />
    <li>servlet 容器通知 <code>ContextLoaderListener</code> 类应用程序正在启动。<br />
    <br />
    <li><code>ContextLoaderListener</code> 类创建一个 Web 应用程序上下文以保存应用程序中特定于 Spring 的资源信息。借助 Spring 的 IOC 框架，您可以装载自己的自定义应用程序上下文。要创建应用程序上下文，将使用名为 <code>ContextLoader</code> 的上下文装载器类装载应用程序上下文。<br />
    <br />
    <li>如果应用程序不需要定义自己的应用程序上下文，则可以使用名为 <code>XMLWebApplicationContext</code> 的类，它是 Spring 框架的一部分并提供可处理 Spring XML 配置文件的功能。Acegi 应用程序使用的是 Spring 的 XML 配置文件，因此本文仅讨论由 <code>XMLWebApplicationContext</code> 类表示的应用程序上下文。在本例中，上下文装载器对 <code>XMLWebApplicationContext</code> 类进行实例化，后者表示您的 Acegi 应用程序的应用程序上下文。上下文装载器还在 Web 应用程序上下文中设置 servlet 上下文（于步骤 1 中创建）的引用。<br />
    <br />
    <li><code>XMLWebApplicationContext</code> 类对 XML 配置文件进行解析，获得关于 Java 类的信息并将信息装载到其他内部对象中。<br />
    <br />
    <li><code>XMLWebApplicationContext</code> 类对 XML 配置文件中指定的所有 Java 类进行实例化。<code>XMLWebApplicationContext</code> 类检查 XML 配置文件中经过配置的 Java bean 是否依赖其他的 Java 对象。如果是的话，<code>XMLWebApplicationContext</code> 类将首先对其他 bean 所依赖的 bean 进行实例化。通过这种方式，<code>XMLWebApplicationContext</code> 类创建了 XML 配置文件中定义的所有 bean 的实例。（注意，步骤 6 假定 XML 配置文件中所有 bean 都不要进行保护，稍后一节将介绍步骤 5 和步骤 6 之间执行的额外步骤，从而保护对此处创建的 Java bean 的访问）。<br />
    <br />
    <li><code>XMLWebApplicationContext</code> 类将所有 bean 保存在一个数组中。 </li>
</ol>
<p>您现在已了解到如何从 XML 配置文件中装载 bean 定义并创建 Java 类的实例。接下来，我将向您介绍 Spring bean 代理并解释它对于保护 Java 类实例的重要性。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section3"><span class="atitle">使用 bean 代理</span></a></p>
<p>上一节讨论了 Spring 的 IOC 框架对 Java 对象进行实例化。要保护对 Java 对象的访问，Spring 的 IOC 框架使用了 <em>bean 代理</em> 的概念。本节首先介绍如何配置 bean 代理，然后演示 Spring 的 IOC 框架如何创建代理对象。</p>
<p><a name="section3.1"><span class="smalltitle">为 Java 对象配置代理</span></a></p>
<p>如果希望创建 bean 代理，Spring IOC 框架要求您对代理创建器 bean 的实例进行配置。Spring 的 IOC 框架使用代理创建器创建代理对象。清单 2 为代理创建器 bean 的配置文件，用于保护名为 <code>privateCatalog</code> 的 Java 对象：</p>
<br />
<a name="listing2"><strong>清单 2. 代理 bean 配置</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            &lt;bean id="proxyCreator"
            class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"&gt;
            &lt;property name="beanNames"&gt;
            &lt;list&gt;
            &lt;value&gt;privateCatalog&lt;/value&gt;
            &lt;!--Names of other beans to be proxied --&gt;
            &lt;/list&gt;
            &lt;/property&gt;
            &lt;property name="interceptorNames"&gt;
            &lt;list&gt;
            &lt;value&gt;privateCatalogSecurityInterceptor&lt;/value&gt;
            &lt;/list&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>如清单 2 所示，<code>&lt;bean&gt;</code> 标记具有一个 <code>class</code> 属性，其值为 <code>org.springframework.aop.framework.autoproxy. BeanNameAutoProxyCreator</code>。<code>BeanNameAutoProxyCreator</code> 类是 Spring IOC 框架的一部分，可以自动创建 bean 代理。Spring 框架提供了 <code>BeanPostProcessor</code> 接口，它提供了一种可扩展机制，允许应用程序编写自己的逻辑来创建 bean 代理。Spring 的 <code>BeanNameAutoProxyCreator</code> 类实现了 <code>BeanPostProcessor</code> 接口并提供所有必需的代理创建逻辑来保护 Java 类。因此，本文中您无需实现 <code>BeanPostProcessor</code> 接口。</p>
<p>在创建 bean 代理时，<code>BeanNameAutoProxyCreator</code> 类为所有由 <code>beanNames</code> 属性定义的 bean 创建代理（参见 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#listing2" cmimpressionsent="1">清单 2</a> 中 <code>&lt;bean&gt;</code> 标记的第一个 <code>&lt;property&gt;</code> 子元素）。<code>beanNames</code> 属性在 <code>&lt;list&gt;</code> 标记中包含一个 bean 名称列表。在 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#listing2" cmimpressionsent="1">清单 2</a> 中，我只对希望为之创建代理的 <code>privateCatalog</code> bean进行了配置。</p>
<p>现在查看 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#listing2" cmimpressionsent="1">清单 2</a> 中 <code>&lt;bean&gt;</code> 标记的第二个 <code>&lt;property&gt;</code> 子元素。它指定了名为 <code>interceptorNames</code> 的代理，它将一个或多个拦截器的名称封装起来。我将在后文详细讨论拦截器概念。现在，只需了解拦截器可以拦截用户并在用户访问 bean 之前实现访问控制策略。</p>
<p>现在，您已了解了如何对希望进行保护的 bean 配置代理。接下来，您将了解 Spring 的 IOC 框架如何在内部为应用程序的 bean 创建代理对象。</p>
<p><a name="section3.2"><span class="smalltitle">Spring IOC 发挥效用</span></a></p>
<p>在 &#8220;<a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#section2" cmimpressionsent="1">使用 Spring 创建 Java 对象</a>&#8221; 的步骤 5 和步骤 6 中，您了解了 <code>XMLWebApplicationContext</code> 类如何从 XML 配置文件中读取 bean 定义并随后创建 bean 实例。在创建 bean 实例之前，<code>XMLWebApplicationContext</code> 类将检查 XML 配置文件是否包含任何代理创建器 bean（即实现 <code>BeanPostProcessor</code> 接口的 bean）配置。如果存在该 bean，它将要求代理创建器为您希望进行保护的 bean 创建 bean 代理。</p>
<a name="proxies">
<p>现在考虑代理创建器如何在内部创建代理对象：</p>
<ol>
    <li>代理创建器（即 <code>BeanNameAutoProxyCreator</code> 类）装载 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#listing2" cmimpressionsent="1">清单 2</a> 中配置的 <code>beanNames</code> 属性文件中指定的所有 bean 名称。<br />
    <br />
    <li>代理创建器使用 bean 名称装载各自的 Java 类，这些类使用了每个 bean 定义的 <code>class</code> 属性。<br />
    <br />
    <li>代理创建器创建 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#listing2" cmimpressionsent="1">清单 2</a> 所示的 <code>interceptorNames</code> 属性中指定的拦截器的实例。<br />
    <br />
    <li>最后，代理创建器创建一个 <code>Cglib2AopProxy</code> 类的实例，将所有 bean 名称（步骤 2）和拦截器（步骤 3）传递到 <code>Cglib2AopProxy</code> 类。<code>Cglib2AopProxy </code>类是 Spring 框架的一部分并用于生成动态代理对象。在本例中，<code>Cglib2AopProxy</code> 类将创建安全 bean 访问控制所需的代理对象。 </li>
</ol>
<p><code>Cglib2AopProxy</code> 类实现了两个名为 <code>AOPProxy</code> 和 <code>MethodInterceptor</code> 的接口。<code>AOPProxy</code> 接口由 Spring 框架提供，表示您希望进行代理的实际 bean，因此它与您的 bean 公开相同的方法。<code>MethodInterceptor</code> 接口也源于 AOP 框架，它包含的方法可以在用户试图访问您已执行代理的 bean 时接受控制权。这意味着 <code>MethodInterceptor</code> 接口处理来自用户的请求以访问执行过代理的 bean。由于 <code>Cglib2AopProxy</code> 类同时实现了 <code>AOPProxy</code> 和 <code>MethodInterceptor</code> 接口，因此它提供了完整的功能，既可以提供经过代理的 bean，也可以处理用户请求以访问代理 bean（参见 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#resources" cmimpressionsent="1">参考资料小节</a> 中有关 AOP 的讨论文章的链接）。</p>
<p>执行完前面的步骤后，您现在具有了所需的代理对象。因此 <code>XMLWebApplicationContext</code> 类将安全 bean 的代理（而不是实际的 bean）保存在 &#8220;<a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#section2" cmimpressionsent="1">使用 Spring 创建 Java 对象</a>&#8221; 的步骤 7 中的同一个数组中。 </p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section4"><span class="atitle">访问执行过代理的 Java 对象</span></a></p>
<p>在前面的几节中，您了解了 Spring 如何创建公有 bean 和私有 bean。出于本文的目的，您可将公有 bean 视为使用代理保护的不安全的私有 bean。现在我们来看一下客户机应用程序为访问公有 bean 和私有 bean 而必须遵循的一系列步骤。</p>
<p>清单 3 展示了 <code>publicCatalog</code> 和 <code>privateCatalog</code> 两个 bean 的 XML 配置。<code>publicCatalog</code> bean 意味着公共访问，因此不需要使用 bean 代理。<code>privateCatalog</code> bean 意味着只能由指定用户访问，因此必须加以保护。我在清单 3 中包含了 <code>privateCatalog</code> bean 的 bean 代理配置：</p>
<br />
<a name="listing3"><strong>清单 3. publicCatalog 和 privateCatalog bean 的 XML 配置</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            &lt;beans&gt;
            &lt;bean id="publicCatalog" class="sample.PublicCatalog"/&gt;
            &lt;bean id="privateCatalog" class="sample.PrivateCatalog"/&gt;
            &lt;!-- proxy configuration for privateCatalog bean --&gt;
            &lt;bean id="proxyCreator"
            class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"&gt;
            &lt;property name="beanNames"&gt;
            &lt;list&gt;
            &lt;value&gt;privateCatalog&lt;/value&gt;
            &lt;!--Names of other beans to be proxied --&gt;
            &lt;/list&gt;
            &lt;/property&gt;
            &lt;property name="interceptorNames"&gt;
            &lt;list&gt;
            &lt;value&gt;privateCatalogSecurityInterceptor&lt;/value&gt;
            &lt;/list&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            &lt;beans&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>应用程序可以使用清单 4 中的代码访问清单 3 中配置的 <code>publicCatalog</code> 和 <code>privateCatalog</code> Java bean。注意，清单 4 中显示的 Java 代码可位于 JSP 页面或位于服务器端 Java 应用程序的 bean 中。</p>
<br />
<a name="listing4"><strong>清单 4. 访问安全和不安全 Java bean 的客户机应用程序代码</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            //Step 1: Fetching an instance of the application context
            XMLWebApplicationContext applicationCtx =
            WebApplicationContextUtils.getWebApplicationContext(
            this.getServletConfig().getServletContext());
            //Step 2: Fetching an insecure bean from the application context
            PublicCatalog publicCatalog =
            (PublicCatalog) applicationCtx.getBean("publicCatalog");
            //Step 3: Calling a method of the insecure bean
            String publicData = publicCatalog.getData();
            //Step 4: Fetching a secure bean from the application context
            PrivateCatalog privateCatalog =
            (PrivateCatalog) applicationCtx.getBean("privateCatalog");
            //Step 5: Calling a method of the secure bean
            String privateData = privateCatalog.getData();
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>下面将进一步讨论清单 4 中的步骤：</p>
<ul>
    <li><strong>步骤 1：取回一个应用程序上下文实例</strong> <br />
    当应用程序希望访问 XML 配置文件中配置的 Java bean 时，它必须取回您在 &#8220;<a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#section2" cmimpressionsent="1">使用 Spring 创建 Java 对象</a>&#8221; 的步骤 4 中见到的 <code>XMLWebApplicationContext</code> 对象。<code>XMLWebApplicationContext</code> 对象包含对 XML 配置文件配置的所有 Java beans 的引用。<br />
    <br />
    <li><strong>步骤 2：从应用程序上下文中取回不安全的 bean</strong> <br />
    您现在具有一个对 <code>XMLWebApplicationContext</code> 对象的引用。<code>XMLWebApplicationContext</code> 类公开了一个 <code>getBean()</code> 方法，它包含 bean 的名称并在数组中查找 &#8220;<a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#section2" cmimpressionsent="1">使用 Spring 创建 Java 对象</a>&#8221; 步骤 7 中准备的 bean。在本例中，该 bean 为 <code>publicCatalog</code>（未执行过代理），因此 <code>XMLWebApplicationContext</code> 将返回实际的 bean。<br />
    <br />
    <li><strong>步骤 3：调用不安全 bean 的方法</strong> <br />
    现在您可以调用步骤 2 中获得的 <code>publicCatalog</code> bean 的任何方法。例如，清单 4 显示的 <code>getData()</code> 方法调用的执行没有应用任何访问控制并向应用程序返回类别数据。<br />
    <br />
    <li><strong>步骤 4：从应用程序上下文取回安全 bean</strong> <br />
    安全 bean 与不安全 bean 的取回方式类似，惟一区别是：当您通过调用 <code>getBean()</code> 方法尝试取回安全 bean 时，您将获得安全对象的代理而不是实际的对象。该代理就是我在 &#8220;<a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#proxies" cmimpressionsent="1">Spring IOC 发挥效用</a>&#8221; 步骤 4 中解释的由 Spring 框架创建的同一个对象。<br />
    <br />
    <li><strong>步骤 5：调用安全 bean 的方法</strong> <br />
    当调用安全 bean 的方法时，您在 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#proxies" cmimpressionsent="1">步骤 4</a> 中获得的代理对象将一个方法调用请求分配给拦截器。拦截器将检查试图访问方法的用户是否具有相应的访问权，从而处理方法调用请求。 </li>
</ul>
<p>您现在应该对 Spring 框架如何创建 Java 对象以及客户机应用程序如何与之交互有了清晰的了解。了解了这些内容后，就更加容易理解并利用 Acegi 的方法安全性拦截器，下一节将具体介绍该主题。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section5"><span class="atitle">配置 Acegi 的方法安全性拦截器</span></a></p>
<p>只要应用程序试图访问由 Acegi 安全系统保护的 bean 方法，请求将被自动传递到 Acegi 的方法安全性拦截器。方法安全性拦截器的作用就是控制对安全 Java bean 的方法的访问。拦截器使用 Acegi 的身份验证和授权框架确认用户是否具有权利调用安全 Java bean 的方法，然后相应地作出响应。</p>
<p>清单 5 展示 Acegi 的方法安全性拦截器的示例配置：</p>
<br />
<a name="listing5"><strong>清单 5. Acegi 的方法安全性拦截器的示例配置</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            &lt;bean id="privateCatalogSecurityInterceptor"
            class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"&gt;
            &lt;property name="authenticationManager"&gt;
            &lt;ref bean="authenticationManager"/&gt;
            &lt;/property&gt;
            &lt;property name="accessDecisionManager"&gt;
            &lt;ref bean="accessDecisionManager"/&gt;
            &lt;/property&gt;
            &lt;property name="objectDefinitionSource"&gt;
            &lt;value&gt;
            sample.PrivateCatalog.getData=ROLE_HEAD_OF_ENGINEERING
            &lt;!-- Roles required by other beans --&gt;
            &lt;/value&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>清单 5 所示的拦截器配置包含三个需要进行配置的属性，可以保护对 Java bean 的访问：<code>authenticationManager</code>、<code>accessDecisionManager</code> 和 <code>objectDefinitionSource</code>。</p>
<p>回忆一下，您在本系列第 1 部分的 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html#section4.2" cmimpressionsent="1">配置身份验证处理过滤器</a> 中曾对 <code>authenticationManager</code> 属性进行了配置。<code>authenticationManager</code> 属性的作用是对用户进行身份验证。</p>
<p>您在本系列的第二篇文章中了解了 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/index.html#section6.1" cmimpressionsent="1">accessDecisionManager</a> 属性。这个访问决策管理器负责制定授权决策。在允许对一个安全 bean 进行访问之前，方法安全拦截器使用 <code>authenticationManager</code> 和 <code>accessDecisionManager</code> 属性对用户进行身份验证和授权。</p>
<p>现在查看 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#listing5" cmimpressionsent="1">清单 5</a> 中配置的 <code>objectDefinitionSource</code> 属性。它类似于第 1 部分中出现的 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html#section4.5" cmimpressionsent="1">objectDefinitionSource</a> 属性。以前的 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html#section4.5" cmimpressionsent="1">objectDefinitionSource</a> 包含类似于 <code>/protected/*</code> 和 <code>/**</code> 这样的 URL，清单 5 中的 <code>objectDefinitionSource</code> 属性指定类和方法名；例如，<code>sample.PrivateCatalog</code> 是之前执行过代理的类的名称，而 <code>getData</code> 是您希望对其控制用户访问的方法的名字。</p>
<p>当用户访问 <code>PrivateCatalog</code> bean 的 <code>getData()</code> 方法时，控制权将自动传递给拦截器。拦截器使用 Acegi 框架检查用户的业务角色是否为 <code>ROLE_HEAD_OF_ENGINEERING</code>（特定于本文的示例）。如果是的话，拦截器将允许对 <code>getData()</code> 方法进行访问。如果拦截器发现用户角色不是 <code>ROLE_HEAD_OF_ENGINEERING</code>，则拒绝访问。</p>
<p>下一节将查看一个示例 Acegi 应用程序，它将实现您目前所了解的所有概念。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section6"><span class="atitle">示例 Acegi 应用程序</span></a></p>
<p>本文的 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#download" cmimpressionsent="1">下载源代码</a> 包含了一个名为 AcegiMethodSecurity 的示例应用程序，可按照以下方法进行配置和部署：</p>
<ol>
    <li>使用用户信息填充 LDAP 服务器。<a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#download" cmimpressionsent="1">下载的示例应用程序</a> 包含一个 LDIF 文件，其中含有预备装载到 LDAP 服务器的用户信息。关于如何将 LDIF 文件导入到 LDAP 服务器，请参考第 2 部分的 &#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/index.html#section2.2" cmimpressionsent="1">填充服务器</a>&#8221; 一节。注意，该应用程序涉及与第 2 部分相同的用户（<code>alice</code>、<code>bob</code> 和 <code>specialUser</code>）。<br />
    <br />
    <li>将本文下载源代码中的 acegiMethodSecurity.war 文件复制到 Tomcat 安装目录中的 webapps 目录。<br />
    <br />
    <li>将 Acegi 的 jar 文件复制到示例应用程序的 WEB-INF/lib 文件夹。（有关内容请参考第 1 部分的 &#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html#section6.2" cmimpressionsent="1">部署和运行应用程序</a>&#8221; 一节。 ）<br />
    <br />
    <li>下载 cglib-full-2.0.2.jar 文件并将其复制到示例应用程序的 WEB-INF/lib 文件夹。 </li>
</ol>
<p>启动 Tomcat 并尝试运行示例应用程序。</p>
<p><a name="section6.1"><span class="smalltitle">运行示例应用程序</span></a></p>
<p>通过从浏览器访问 http://localhost:8080/acegiMethodSecurity URL 可调用示例应用程序。AcegiMethodSecurity 显示的索引页面包含两个链接（<strong>Catalog</strong> 和 <strong>Login</strong>），如图 2 所示：</p>
<br />
<a name="figure2"><strong>图 2. 示例应用程序的主页面</strong></a><br />
<img height="248" alt="示例应用程序的主页面" src="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/Figure2.jpg" width="402" /> <br />
<p>当单击应用程序的 <strong>Catalog</strong> 链接时，它将要求您进行登录。如果以 <code>alice</code> 或 <code>specialUser</code> 的身份进行登录，示例应用程序将提供<em>完整的</em> 类别，包括公有数据和私有数据。这是因为在 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#listing5" cmimpressionsent="1">清单 5</a> 中，您对方法安全性拦截器进行了配置，允许用户使用 <code>ROLE_HEAD_OF_ENGINEERING</code> 访问私有类别，而 <code>alice</code> 和 <code>specialUser</code> 都具有该访问权。另一方面，如果您以 <code>bob</code> 的身份登录，示例应用程序将仅显示公有数据。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="7"><span class="atitle">为通过身份验证的用户分配额外角色</span></a></p>
<p>本节将演示经过增强的示例应用程序。增强后的示例应用程序将展示 Acegi 如何使您能够在运行时向通过身份验证的用户临时分配额外角色。 </p>
<p>当安全 bean（例如 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#listing3" cmimpressionsent="1">清单 3</a> 的 <code>privateCatalog</code> bean）要访问一个原创资源时，您可能需要使用额外的角色。例如，您可能考虑到您的安全 bean 需要通过 Java 的 Remote Method Invocation (RMI) 框架或一个 Web 服务访问某个远程应用程序。访问安全 bean 的用户不会占用远程应用程序要求访问用户所具备的业务角色。</p>
<p>在本例中，Acegi 首先检查用户是否经过授权来访问安全 bean。之后，Acegi 允许用户访问安全 bean。当安全 bean 试图访问远程服务时，它需要使用额外的业务角色。如果访问安全 bean 的用户不具备额外角色，安全 bean 就不能成功访问远程服务。</p>
<p><a name="section7.1"><span class="smalltitle">run-as-replacement 机制</span></a></p>
<p>Acegi 框架提供了一种名为 <em>run-as-replacement</em> 的简单机制，允许您仅在方法调用期间为通过身份验证的用户配置一个或多个额外角色。您可以使用 run-as-replacement 机制为访问远程应用程序的安全 bean 配置额外角色。这意味着只要安全 bean 需要访问远程应用程序，Acegi 将为用户装载额外角色，从而允许安全 bean 访问远程应用程序。</p>
<p>清单 6 对 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#listing5" cmimpressionsent="1">清单 5</a> 中的方法安全性拦截器的配置进行了增强。增强后的配置使用了 run-as-replacement 机制。</p>
<br />
<a name="listing6"><strong>清单 6. Acegi 方法安全性拦截器的增强配置</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            &lt;bean id="privateCatalogSecurityInterceptor"
            class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"&gt;
            &lt;property name="authenticationManager"&gt;
            &lt;ref bean="authenticationManager"/&gt;
            &lt;/property&gt;
            &lt;property name="accessDecisionManager"&gt;
            &lt;ref bean="accessDecisionManager"/&gt;
            &lt;/property&gt;
            <span class="boldcode">&lt;property name="runAsManager"&gt;</span>
            <br />
            <span class="boldcode">    &lt;bean id="runAsManager"
            class="org.acegisecurity.runas.RunAsManagerImpl"&gt;</span>
            <br />
            <span class="boldcode">         &lt;property name="key"&gt;</span>
            <span class="boldcode">            &lt;value&gt;myKeyPass&lt;/value&gt;</span>
            <span class="boldcode">         &lt;/property&gt;</span>
            <span class="boldcode">    &lt;/bean&gt;</span>
            <span class="boldcode">  &lt;/property&gt;</span>
            &lt;property name="objectDefinitionSource"&gt;
            &lt;value&gt;
            sample.PrivateCatalog.getData=ROLE_HEAD_OF_ENGINEERING,<span class="boldcode">RUN_AS_MANAGER</span>
            &lt;/value&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>清单 6 使用粗体显示了两处增强（与 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#listing5" cmimpressionsent="1">清单 5</a> 相比）。第一处增强为 <code>runAsManager</code> 属性。<code>runAsManager</code> 属性的作用是向通过身份验证的用户动态添加角色。出于这个目的，<code>runAsManager</code> 属性包含了 <code>RunAsManagerImpl</code> bean 的定义。<code>RunAsManagerImpl</code> bean 只有在满足下面的条件时才可变为活跃状态：在 <code>objectDefinitionSource</code> 方法的角色定义中找到以 <code>RUN_AS_</code> 为前缀的角色。例如，<code>PrivateCatalog.getData()</code> 方法的角色定义（<a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#listing6" cmimpressionsent="1">清单 6</a> 中以粗体显示的第二处增强）具有一个 <code>RUN_AS_MANAGER</code> 角色。</p>
<p><code>RunAsManagerImpl</code> bean 包含一个名为 <code>key</code> 的属性，它封装的加密键用于确保只将额外的角色作为 run-as-replacement 程序的一部分生成。</p>
<p>当用户调用 <code>getData()</code> 方法时，<code>RunAsManagerImpl</code> bean 变为活跃状态并创建名为 <code>RUN_AS_MANAGER</code> 的额外角色，从而启用 <code>getData()</code> 方法访问远程应用程序。</p>
<p><a name="section7.2"><span class="smalltitle">增强的方法安全性</span></a></p>
<p>本文的 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#download" cmimpressionsent="1">下载源代码</a> 包含一个名为 <code>EnhancedAcegiMethodSecurity</code> 的示例应用程序，它可以演示 run-as-replacement 机制和程序。该应用程序将显示一个具有 Catalog 链接的索引页面。如果单击 <strong>Catalog</strong> 链接，将要求进行登录。</p>
<p>登录后，<code>EnhancedAcegiMethodSecurity</code> 应用程序将为您提供登录用户及其角色的完整信息。例如，如果以 <code>alice</code> 或 <code>specialUser</code> 身份登录，将向您显示用户的所有业务角色，包括额外创建的临时的 <code>RUN_AS_MANAGER</code> 角色。</p>
<p><a name="section8"><span class="atitle">结束语</span></a></p>
<p>在这份共分三部分的系列文章中，我介绍了如何使用 Acegi 安全系统增强基于 URL 的安全性和基于方法的安全性。您了解了如何设计访问控制策略并将它们托管在目录服务器中，如何对 Acegi 进行配置以与目录服务器进行通信，以及如何根据托管在服务器的访问控制策略制定身份验证和授权决策。</p>
<p>本系列的最后一篇文章主要介绍使用基于方法的安全性保护 Java 类实例。文章还解释了 Acegi 和 Spring 如何在内部创建和代理 Java 对象以及 bean 代理如何实现访问控制。文章包含了两个示例应用程序，您可以使用它们进一步研究本系列中学到的概念，更多有关使用 Acegi 保护 Java 应用程序的内容，请参见 <a href="http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?#resources" cmimpressionsent="1">参考资料</a> 小节。</p>
<br />
<br />
来自:http://www-128.ibm.com/developerworks/cn/java/j-acegi3/?
 <img src ="http://www.blogjava.net/waterjava/aggbug/199312.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/waterjava/" target="_blank">狼爱上狸</a> 2008-05-08 18:47 <a href="http://www.blogjava.net/waterjava/archive/2008/05/08/199312.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>acegi,IBM的Acegi Security System（2） </title><link>http://www.blogjava.net/waterjava/archive/2008/05/08/199311.html</link><dc:creator>狼爱上狸</dc:creator><author>狼爱上狸</author><pubDate>Thu, 08 May 2008 10:43:00 GMT</pubDate><guid>http://www.blogjava.net/waterjava/archive/2008/05/08/199311.html</guid><wfw:comment>http://www.blogjava.net/waterjava/comments/199311.html</wfw:comment><comments>http://www.blogjava.net/waterjava/archive/2008/05/08/199311.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/waterjava/comments/commentRss/199311.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/waterjava/services/trackbacks/199311.html</trackback:ping><description><![CDATA[<p>2007 年 6 月 21 日</p>
<blockquote>了解了 Acegi 安全系统（Acegi Security System）的 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html" cmimpressionsent="1">基础知识</a> 后，我们将介绍该系统的更加高级的应用。在本文中，Bilal Siddiqui 向您展示了如何结合使用 Acegi 和一个 LDAP 目录服务器，实现灵活的具有高性能的 Java&#8482; 应用程序的安全性。还将了解如何编写访问控制策略并将其存储在 ApacheDS 中，然后配置 Acegi 使其与目录服务器交互，从而实现身份验证和授权的目的。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p>这期共分三部分的系列文章介绍了如何使用 Acegi 安全系统保护 Java 企业应用程序。在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html" cmimpressionsent="1">本系列第一篇文章</a> 中，我介绍了 Acegi 并解释了如何使用安全过滤器实现一个简单的基于 URL 的安全系统。在第二篇文章中，我将讨论 Acegi 的更加高级的应用，首先我将编写一个访问控制策略并将其存储在 ApacheDS 中，ApacheDS 是一个开源的 LDAP 目录服务器。我还将展示配置 Acegi 的方法，使它能够与目录服务器交互并实现您的访问控制策略。本文的结尾提供了一个示例应用程序，它使用 ApacheDS 和 Acegi 实现了一个安全的访问控制策略。</p>
<p>实现访问控制策略通常包含两个步骤：</p>
<ol>
    <li>将有关用户和用户角色的数据存储在目录服务器中。
    <li>编写安全代码，它将定义有权访问并使用数据的人员。 </li>
</ol>
<p>Acegi 将减轻代码编写的工作，因此在这篇文章中，我将展示如何将用户和用户角色信息存储到 ApacheDS 中，然后实现这些信息的访问控制策略。在该系列的最后一篇文章中，我将展示如何配置 Acegi，实现对 Java 类的安全访问。 </p>
<p>您可以在本文的任何位置 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#download" cmimpressionsent="1">下载样例应用程序</a>。参见 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#resources" cmimpressionsent="1">参考资料</a> 下载 Acegi、Tomcat 和 ApacheDS，您需要使用它们运行样例代码和示例应用程序。</p>
<p><a name="section1.0"><span class="atitle">LDAP 基础知识</span></a></p>
<p>轻量级目录访问协议（Lightweight Directory Access Protocol，LDAP）可能是最流行的一种定义数据格式的协议，它针对常见的目录操作，例如对存储在目录服务器中的信息执行的读取、编辑、搜索和删除操作。本节将简要解释为什么目录服务器是属性文件存储安全信息的首选，并展示如何在 LDAP 目录中组织和托管用户信息。 </p>
<p><a name="section1.1"><span class="smalltitle">为什么要使用目录服务器？</span></a></p>
<p>本系列第一部分向您介绍了一种简单的方法，可以将用户信息以属性文件的形式保存起来（参见 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html#listing6" cmimpressionsent="1">第 1 部分，清单 6</a>）。属性文件以文本格式保存用户名、密码和用户角色。对于大多数真实应用程序而言，使用属性文件存储安全信息远远不够。各种各样的理由表明，目录服务器通常都是更好的选择。其中一个原因是，真实的企业应用程序可以被大量用户访问 —— 通常是几千名用户，如果应用程序将其部分功能公开给用户和供应商时更是如此。频繁搜索文本文件中随意存储的信息，这样做的效率并不高，但是目录服务器对这类搜索进行了优化。 </p>
<p><a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html#listing6" cmimpressionsent="1">第 1 部分的清单 6</a> 中的属性文件演示了另一个原因，该文件组合了用户和角色。在真实的访问控制应用程序中，您通常都需要分别定义和维护用户和角色信息，这样做可以简化用户库的维护。目录服务器为更改或更新用户信息提供了极大的灵活性，例如，反映职位升迁或新聘用人员。参见 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#resources" cmimpressionsent="1">参考资料</a> 以了解更多关于目录服务器的使用及其优点的信息。</p>
<p><a name="section1.2"><span class="smalltitle">LDAP 目录设置</span></a></p>
<p>如果希望将用户信息存储在一个 LDAP 目录中，您需要理解一些有关目录设置的内容。本文并没有提供对 LDAP 的完整介绍（参见 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#resources" cmimpressionsent="1">参考资料</a>），而是介绍了一些在尝试结合使用 Acegi 和 LDAP 目录之前需要了解的基本概念。 </p>
<p>LDAP 目录以节点树的形式存储信息，如图 1 所示：</p>
<br />
<a name="fig1"><strong>图 1. LDAP 目录的树状结构</strong></a><br />
<img height="303" alt="图 1. LDAP 目录的树状结构" src="http://www.ibm.com/developerworks/cn/java/j-acegi2/figure1.jpg" width="248" /> <br />
<p>在图 1 中，根节点的名称为 <code>org</code>。根节点可以封装与不同企业有关的数据。例如，本系列第 1 部分开发的制造业企业被显示为 <code>org</code> 节点的直接子节点。该制造业企业具有两个名为 <code>departments</code> 和 <code>partners</code> 的子节点。</p>
<p><code>partners</code> 子节点封装了不同类型的合作伙伴。图 1 所示的三个分别为 <code>customers</code>、<code>employees</code> 和 <code>suppliers</code>。注意，这三种类型的合作伙伴其行为与企业系统用户一样。每一种类型的用户所扮演的业务角色不同，因此访问系统的权利也不同。 </p>
<p>类似地，<code>departments</code> 节点包含该制造业企业的不同部门的数据 —— 例如 <code>engineering</code> 和 <code>marketing</code> 字节点。每个部门节点还包含一组或多组用户。在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#fig1" cmimpressionsent="1">图 1</a> 中，<code>engineers</code> 组是 <code>engineering</code> 部门的子节点。</p>
<p>假设每个部门的子节点表示一组用户。因此，部门节点的子节点具有不同的用户成员。例如，设计部门的所有工程师都是 <code>engineering</code> 部门内 <code>engineers</code> 组的成员。</p>
<p>最后，注意 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#fig1" cmimpressionsent="1">图 1</a> 中 <code>departments</code> 节点的最后一个子节点。<code>specialUser</code> 是一名用户，而非一组用户。在目录设置中，像 <code>alice</code> 和 <code>bob</code> 之类的用户一般都包含在 <code>partners</code> 节点中。我将这个特殊用户包含在 <code>departments</code> 节点中，以此证明 Acegi 允许用户位于 LADP 目录中任何地点的灵活性。稍后在本文中，您将了解如何配置 Acegi 以应用 <code>specialUser</code>。</p>
<p><a name="section1.3"><span class="smalltitle">使用专有名称</span></a></p>
<p>LDAP 使用<em>专有名称</em>（DN）的概念来识别 LDAP 树上特定的节点。每个节点具有惟一的 DN，它包含该节点完整的层次结构信息。例如，图 2 展示了图 1 中的一些节点的 DN：</p>
<br />
<a name="fig2"><strong>图 2. LDAP 目录节点的专有名称</strong></a><br />
<img height="242" alt="图 2. LDAP 目录节点的专有名称" src="http://www.ibm.com/developerworks/cn/java/j-acegi2/figure2.jpg" width="493" /> <br />
<p>首先，注意图 2 中根节点的 DN。它的 DN 为 <code>dc=org</code>，这是与 <code>org</code> 根节点相关的属性值对。每个节点都有若干个与之相关的属性。<code>dc</code> 属性代表 &#8220;domain component&#8221; 并由 LDAP RFC 2256 定义（参见 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#resources" cmimpressionsent="1">参考资料</a> 中有关官方 RFC 文档的链接），LDAP 目录中的根节点通常表示为一个域组件。</p>
<p>每个 LDAP 属性是由 RFC 定义的。LDAP 允许使用多个属性创建一个 DN，但是本文的示例只使用了以下 4 个属性：</p>
<ul>
    <li><code>dc</code>（域组件）
    <li><code>o</code>（组织）
    <li><code>ou</code>（组织单元）
    <li><code>uid</code>（用户 ID） </li>
</ul>
<p>示例使用 <code>dc</code> 表示域，用 <code>o</code> 表示组织名称，<code>ou</code> 表示组织的不同单元，而 <code>uid</code> 表示用户。</p>
<p>由于 <code>org</code> 是根节点，其 DN 只需指定自身的名称（<code>dc=org</code>）。比较一下，<code>manufacturingEnterprise</code> 节点的 DN 是 <code>o=manufacturingEnterprise,dc=org</code>。当向下移动节点树时，每个父节点的 DN 被包含在其子节点的 DN 中。 </p>
<p><a name="section1.4"><span class="smalltitle">将属性分组</span></a></p>
<p>LDAP 将相关的属性类型分到对象类中。例如，名为 <code>organizationalPerson</code> 的对象类所包含的属性定义了在组织内工作的人员（例如，职称、常用名、邮寄地址等等）。 </p>
<p>对象类使用继承特性，这意味着 LDAP 定义了基类来保存常用属性。然后子类再对基类进行扩展，使用其定义的属性。LDAP 目录中的单个节点可以使用若干个对象类：本文的示例使用了以下几个对象类： </p>
<ul>
    <li><strong>top</strong> 对象类是 LDAP 中所有对象类的基类。<br />
    <br />
    <li>当其他对象类都不适合某个对象时，将使用 <strong>domain</strong> 对象类。它定义了一组属性，任何一个属性都可以用来指定一个对象。其 <code>dc</code> 属性是强制性的。<br />
    <br />
    <li><strong>organization</strong> 对象类表示组织节点，例如 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#fig2" cmimpressionsent="1">图 2</a> 中的 <code>manufacturingEnterprise</code>。<br />
    <br />
    <li><strong>organizationalUnit</strong> 对象类表示组织内的单元，例如 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#fig1" cmimpressionsent="1">图 1</a> 中的 <code>departments</code> 节点及其子节点。 <br />
    <br />
    <li><strong>groupOfNames</strong> 对象类表示一组名称，例如某部门职员的名称。它具有一个 <code>member</code> 属性，该属性包含一个用户列表。<a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#fig1" cmimpressionsent="1">图 1</a> 中所有的组节点（例如 <code>engineers</code> 节点）使用 <code>member</code> 属性指定该组的成员。而且，示例使用 <code>groupOfNames</code> 对象类的 <code>ou</code>（组织单元）属性指定组用户的业务角色。<br />
    <br />
    <li><strong>organizationalPerson</strong> 对象类表示组织内某个职员（例如 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#fig1" cmimpressionsent="1">图 1</a> 中的 <code>alice</code> 节点）。 </li>
</ul>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section2.0"><span class="atitle">使用 LDAP 服务器</span></a></p>
<p>在真实的应用程序中，通常将有关系统用户的大量信息托管在一个 LDAP 目录中。例如，将存储每个用户的用户名、密码、职称、联系方式和工资信息。为简单起见，下面的例子将只向您展示如何保存用户名和密码。 </p>
<p>如前所述，示例使用 ApacheDS（一种开源的 LDAP 目录服务器）演示了 Acegi 是如何使用 LDAP 目录的。示例还使用了一个开源的 LDAP 客户机（名为 JXplorer）执行简单的目录操作，例如将信息托管在 ApacheDS 上。参见 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#resources" cmimpressionsent="1">参考资料</a> 以下载 ApacheDS、JXplorer 并了解更多有关两者协作的信息。 </p>
<p><a name="section2.1"><span class="smalltitle">在 ApacheDS 创建一个根节点</span></a></p>
<p>要创建 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#fig1" cmimpressionsent="1">图 1</a> 所示的节点树，必须首先在 ApacheDS 中创建一个根节点 <code>org</code>。ApacheDS 为此提供了一个 XML 配置文件。XML 配置文件定义了一组可进行配置的 bean，从而根据应用程序的需求定制目录服务器的行为。本文只解释创建根节点所需的配置。</p>
<p>可以在 ApacheDS 安装中的 <code>conf</code> 文件夹找到名为 <code>server.xml</code> 的 XML 配置文件。打开文件后，会发现很多 bean 配置类似于 Acegi 的过滤器配置。查找名为 <code>examplePartitionsConfiguration</code> 的 bean。该 bean 控制 ApacheDS 上的分区。当创建新的根节点时，实际上将在 LDAP 目录上创建一个新的分区。</p>
<p>编辑 <code>examplePartitionConfiguration</code> bean 以创建 <code>org</code> 根节点，如清单 1 所示：</p>
<br />
<a name="listing1"><strong>清单 1. 编辑模式的 examplePartitionConfiguration bean 配置</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="examplePartitionConfiguration" class=
            "org.apache.directory.server.core.partition.impl.btree.MutableBTreePartitionConfiguration"
            &gt;
            &lt;property name="suffix"&gt;&lt;value&gt;dc=org&lt;/value&gt;&lt;/property&gt;
            &lt;property name="contextEntry"&gt;
            &lt;value&gt;
            objectClass: top
            objectClass: domain
            dc: org
            &lt;/value&gt;
            &lt;/property&gt;
            &lt;!-- Other properties of the examplePartitionConfiguration bean, which you don't
            need to edit. --&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>清单 1 编辑了 <code>examplePartitionConfiguration</code> bean 的两个属性：</p>
<ul>
    <li>一个属性名为 <code>suffix</code>，它定义根条目的 DN。<br />
    <br />
    <li>另一个属性名为 <code>contextEntry</code>，定义 <code>org</code> 节点将使用的对象类。注意，<code>org</code> 根节点使用两个对象类：<code>top</code> 和 <code>domain</code>。 </li>
</ul>
<p>本文的 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#download" cmimpressionsent="1">源代码下载</a> 部分包含了编辑模式的 server.xml 文件。如果希望继续学习本示例，请将 server.xml 文件从源代码中复制到您的 ApacheDS 安装目录中的正确位置，即 <code>conf</code> 文件夹。</p>
<p>图 3 所示的屏幕截图展示了在 ApacheDS 中创建根节点后，JXplorer 是如何显示该根节点的：</p>
<br />
<a name="fig3"><strong>图 3. JXplorer 显示根节点</strong></a><br />
<img height="372" alt="图 3. JXplorer 显示根节点" src="http://www.ibm.com/developerworks/cn/java/j-acegi2/figure3.jpg" width="509" /> <br />
<p><a name="section2.2"><span class="smalltitle">填充服务器</span></a></p>
<p>设置 LDAP 服务器的下一步是使用用户和组信息填充服务器。您可以使用 JXplorer 在 ApacheDS 中逐个创建节点，但是使用 LDAP Data Interchange Format (LDIF) 填充服务器会更加方便。LDIF 是可被大多数 LDAP 实现识别的常见格式。developerWorks 文章很好地介绍了 LDIF 文件的内容，因此本文将不再做详细说明。（参见 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#resources" cmimpressionsent="1">参考资料</a> 中有关 LDIF 的详细资料。）</p>
<p>您可以在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#download" cmimpressionsent="1">源代码下载</a> 部分查看 LDIF 文件，它表示 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#fig1" cmimpressionsent="1">图 1</a> 所示的用户和部门。您可以使用 JXplorer 将 LDIF 文件导入到 ApacheDS。要导入 LDIF 文件，在 JXplorer 中使用 <code>LDIF</code> 菜单，如图 4 所示：</p>
<br />
<a name="fig4"><strong>图 4. 将 LDIF 文件导入到 ApacheDS</strong></a><br />
<img height="286" alt="图 4. 将 LDIF 文件导入到 ApacheDS" src="http://www.ibm.com/developerworks/cn/java/j-acegi2/figure4.jpg" width="528" /> <br />
<p>将 LDIF 文件导入到 ApacheDS 之后，JXplorer 将显示用户节点和部门节点树，如 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#fig1" cmimpressionsent="1">图 1</a> 所示。现在您可以开始配置 Acegi，使其能够与您的 LDAP 服务器通信。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section3.0"><span class="atitle">为 LDAP 实现配置 Acegi</span></a></p>
<p>回想一下第 1 部分，其中 Acegi 使用身份验证处理过滤器（Authentication Processing Filter，APF）进行身份验证。APF 执行所有后端身份验证处理任务，例如从客户机请求中提取用户名和密码，从后端用户库读取用户参数，以及使用这些信息对用户进行身份验证。</p>
<p>您在第 1 部分中为属性文件实现配置了 APF，现在您已将用户库存储在 LDAP 目录中，因此必须使用不同的方式配置过滤器来和 LDAP 目录进行通信。首先看一下清单 2，它展示了在第 1 部分中的 &#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html#section4.2" cmimpressionsent="1">Authentication Processing Filter</a>&#8221; 一节中如何为属性文件实现配置 APF 过滤器：</p>
<br />
<a name="listing2"><strong>清单 2. 为属性文件配置 APF</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="authenticationProcessingFilter"
            class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"&gt;
            &lt;property name="authenticationManager" ref="authenticationManager" /&gt;
            &lt;property name="authenticationFailureUrl"
            value="/login.jsp?login_error=1" /&gt;
            &lt;property name="defaultTargetUrl"
            value="/index.jsp" /&gt;
            &lt;property name="filterProcessesUrl"
            value="/j_acegi_security_check" /&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>查看一下清单 2，您曾经为 APF 提供了 4 个参数。您只需在 LDAP 服务器中为存储重新配置第一个参数（<code>authenticationManager</code>）即可。其他三个参数保持不变。</p>
<p><a name="section3.1"><span class="smalltitle">配置身份验证管理器</span></a></p>
<p>清单 3 展示了如何配置 Acegi 的身份验证管理器，以实现与 LDAP 服务器的通信：</p>
<br />
<a name="listing3"><strong>清单 3. 为 LDAP 配置 Acegi 的身份验证管理器</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="authenticationManager"
            class="org.acegisecurity.providers.ProviderManager"&gt;
            &lt;property name="providers"&gt;
            &lt;list&gt;
            &lt;ref local="ldapAuthenticationProvider" /&gt;
            &lt;/list&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>在清单 3 中，<code>org.acegisecurity.providers.ProviderManager</code> 是一个管理器类，它管理 Acegi 的身份验证过程。为此，身份验证管理器需要一个或多个身份验证提供者。您可以使用管理器 bean 的提供者属性来配置一个或多个提供者。清单 3 只包含了一个提供者，即 LDAP 身份验证提供者。 </p>
<p>LDAP 身份验证提供者处理所有与后端 LDAP 目录的通信。您必须对其进行配置，下一节内容将讨论该主题。 </p>
<p><a name="section3.2"><span class="smalltitle">配置 LDAP 身份验证提供者</span></a></p>
<p>清单 4 展示了 LDAP 身份验证提供者的配置： </p>
<br />
<a name="listing4"><strong>清单 4. 配置 LDAP 身份验证提供者</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="ldapAuthenticationProvider"
            class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider"&gt;
            &lt;constructor-arg&gt;&lt;ref local="authenticator"/&gt;&lt;/constructor-arg&gt;
            &lt;constructor-arg&gt;&lt;ref local="populator"/&gt;&lt;/constructor-arg&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>注意 LDAP 身份验证提供者类的名称为 <code>org.acegisecurity.providers.ldap.LdapAuthenticationProvider </code>。其构造函数包含两个参数，使用两个 <code>&lt;constructor-arg&gt;</code> 标记的形式，如清单 4 所示。</p>
<p><code>LdapAuthenticationProvider</code> 构造函数的第一个参数是 <code>authenticator</code>，该参数通过检查用户的用户名和密码对 LDAP 目录的用户进行身份验证。完成身份验证后，第二个参数 <code>populator</code> 将从 LDAP 目录中检索有关该用户的访问权限（或业务角色）信息。 </p>
<p>以下小节将向您展示如何配置验证器和填充器 bean。 </p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section4.0"><span class="atitle">配置验证器</span></a></p>
<p><code>authenticator</code> bean 将检查具有给定用户名和密码的用户是否存在于 LDAP 目录中。Acegi 提供了名为 <code>org.acegisecurity.providers.ldap.authenticator.BindAuthenticator</code> 的验证器类，它将执行验证用户名和密码所需的功能。</p>
<p>配置 <code>authenticator</code> bean，如清单 5 所示：</p>
<br />
<a name="listing5"><strong>清单 5. 配置验证器 bean</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="authenticator"
            class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator"&gt;
            &lt;constructor-arg&gt;&lt;ref local="initialDirContextFactory"/&gt;&lt;/constructor-arg&gt;
            &lt;property name="userDnPatterns"&gt;
            &lt;list&gt;
            &lt;value&gt;uid={0},ou=employees,ou=partners&lt;/value&gt;
            &lt;value&gt;uid={0},ou=customers,ou=partners&lt;/value&gt;
            &lt;value&gt;uid={0},ou=suppliers,ou=partners&lt;/value&gt;
            &lt;/list&gt;
            &lt;/property&gt;
            &lt;property name="userSearch"&gt;&lt;ref local="userSearch"/&gt;&lt;/property&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>在清单 5 中，<code>BindAuthenticator</code> 构造函数具有一个参数，使用 <code>&lt;constructor-arg&gt;</code> 标记的形式。清单 5 中参数的名称为 <code>initialDirContextFactory</code>。该参数实际上是另一个 bean，稍后您将学习如何配置该 bean。</p>
<p>目前为止，只知道 <code>initialDirContextFactory</code> bean 的作用就是为稍后的搜索操作指定初始上下文。初始上下文是一个 DN，它指定了 LDAP 目录内某个节点。指定初始上下文后，将在该节点的子节点中执行所有的搜索操作（例如查找特定用户）。</p>
<p>例如，回到 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#fig2" cmimpressionsent="1">图 2</a> 中查看 <code>partners</code> 节点，它的 DN 是 <code>ou=partners,o=manufacturingEnterprise,dc=org</code>。如果将 <code>partners</code> 节点指定为初始上下文，Acegi 将只在 <code>partners</code> 节点的子节点中查找用户。 </p>
<p><a name="section4.1"><span class="smalltitle">指定 DN 模式</span></a></p>
<p>除配置 <code>BindAuthenticator</code> 构造函数外，还必须配置 <code>authenticator</code> bean 的两个属性（<a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing5" cmimpressionsent="1">清单 5</a> 中的两个 <code>&lt;property&gt;</code> 标记）。</p>
<p>第一个 <code>&lt;property&gt;</code> 标记定义了一个 <code>userDnPatterns</code> 属性，它封装了一个或多个 DN 模式列表。<em>DN 模式</em> 指定了一组具有类似特性的 LDAP 节点（例如 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#fig2" cmimpressionsent="1">图 2</a> 所示的 <code>employees</code> 节点的所有子节点）。</p>
<p>Acegi 的身份验证器从 <code>authenticator</code> bean 的 <code>userDnPatterns</code> 属性中配置的每个 DN 模式构造了一个 DN。例如，查看 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing5" cmimpressionsent="1">清单 5</a> 中配置的第一个模式，即 <code>uid={0},ou=employees,ou=partners</code>。在进行身份验证的时候，<code>authenticator</code> bean 使用用户提供的用户名（比如 <code>alice</code>）替换了 <code>{0}</code>。使用用户名取代了 <code>{0}</code> 之后，DN 模式将变为相对 DN（RDN）<code>uid=alice,ou=employees,ou=partners</code>，它需要一个初始上下文才能成为 DN。</p>
<p>例如，查看 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#fig2" cmimpressionsent="1">图 2</a> 中的 <code>alice's</code> 条目。该条目是 <code>employees</code> 节点的第一个子节点。它的 DN 是 <code>uid=alice,ou=employees,ou=partners,o=manufacturingEnterprise, dc=org</code>。如果使用 <code>o=manufacturingEnterprise,dc=org</code> 作为初始上下文并将其添加到 RDN <code>uid=alice,ou=employees,ou=partners</code> 之后，将获得 alice 的 DN。</p>
<p>使用这种方法通过 DN 模式构建了用户的 DN 后，<code>authenticator</code> 将把 DN 和用户密码发送到 LDAP 目录。目录将检查该 DN 是否具有正确的密码。如果有的话，用户就可以通过身份验证。这个过程在 LDAP 术语中被称为 <em>bind 身份验证</em>。LDAP 还提供了其他类型的身份验证机制，但是本文的示例只使用了 bind 身份验证。</p>
<p>如果目录中并没有第一个 DN 模式创建的 DN，<code>authenticator</code> bean 尝试使用列表中配置的第二个 DN 模式。依此类推，<code>authenticator</code> bean 将尝试所有的 DN 模式来为进行身份验证的用户构造正确的用户 DN。</p>
<p><a name="section4.2"><span class="smalltitle">搜索过滤器</span></a></p>
<p>回想一下较早的章节 &#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#section1.2" cmimpressionsent="1">LDAP 目录设置</a>&#8221;，我在将用户信息存储到 LDAP 目录时添加了一点灵活性。方法是在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#fig1" cmimpressionsent="1">图 1</a> 所示的 <code>departments</code> 节点内创建一个特定用户（<code>specialUser</code>）。</p>
<p>如果试图使用 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing5" cmimpressionsent="1">清单 5</a> 中配置的任何一种 DN 模式创建特定用户的 DN，您会发现没有一种 DN 模式可用。因此，当用户尝试登录时，Acegi 的 <code>authenticator</code> bean 将不能够构造正确的 DN，从而无法对该用户进行身份验证。</p>
<p>通过允许您指定搜索过滤器，Acegi 能够处理类似的特殊情况。身份验证器 bean 使用搜索过滤器查找不能够通过 DN 模式构造 DN 进行身份验证的用户。 </p>
<p><a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing5" cmimpressionsent="1">清单 5</a> 中的第二个 <code>&lt;property&gt;</code> 标记具有一个 <code>&lt;ref&gt;</code> 子标记，它引用名为 <code>userSearch</code> 的 bean。<code>userSearch</code> bean 指定搜索查询。清单 6 展示了如何配置 <code>userSearch</code> bean 来处理特定用户：</p>
<br />
<a name="listing6"><strong>清单 6. 配置搜索查询以搜索特定用户</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="userSearch"
            class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch"&gt;
            &lt;constructor-arg&gt;
            &lt;value&gt;ou=departments&lt;/value&gt;
            &lt;/constructor-arg&gt;
            &lt;constructor-arg&gt;
            &lt;value&gt;(uid={0})&lt;/value&gt;
            &lt;/constructor-arg&gt;
            &lt;constructor-arg&gt;
            &lt;ref local="initialDirContextFactory" /&gt;
            &lt;/constructor-arg&gt;
            &lt;property name="searchSubtree"&gt;
            &lt;value&gt;true&lt;/value&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><strong>搜索查询的参数</strong> <br />
<br />
清单 6 展示了 <code>userSearch</code> bean 是 <code>org.acegisecurity.ldap.search.FilterBasedLdapUserSearch</code> 类的一个实例，该类的构造函数具有三个参数。第一个参数指定 <code>authenticator</code> 在哪个节点中搜索用户。第一个参数的值为 <code>ou=departments</code>，该值是一个 RDN，指定了 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#fig2" cmimpressionsent="1">图 2</a> 所示的 <code>departments</code> 节点。</p>
<p>第二个参数 <code>(uid={0})</code> 指定了一个搜索过滤器。由于使用 <code>uid</code> 属性指定用户，因此可以通过查找 <code>uid</code> 属性具有特定值的节点来查找用户。正如您所料，花括号里面的 0 向 Acegi 表示使用进行身份验证的用户的用户名（本例中为 <code>specialUser</code>）替换 <code>{0}</code>。 </p>
<p>第三个参数是对讨论 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing5" cmimpressionsent="1">清单 5</a> 中的 <code>BindAuthenticator</code> 构造函数时引入的相同初始上下文的引用。回想一下，当指定了初始上下文后，稍后将在该初始上下文节点的子节点内进行所有的搜索操作。注意，应将指定为 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing5" cmimpressionsent="1">清单 5</a> 中第一个参数（<code>ou=departments</code>）的值的 RDN 前加到初始上下文。</p>
<p>除了这三个构造器参数，清单 6 所示的 <code>userSearch</code> bean 还具有一个名为 <code>searchSubtree</code> 的属性。如果将其值指定为 <code>true</code>，搜索操作将包括节点的子树（即所有子节点、孙节点、孙节点的子节点等），该节点被指定为构造函数的第一个参数的值。 </p>
<p><code>authenticator</code> bean 的配置完成后，下一步将查看 <code>populator</code> bean 的配置，如 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing4" cmimpressionsent="1">清单 4</a> 所示。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section5.0"><span class="atitle">配置 populator</span></a></p>
<p><code>populator</code> bean 将读取已经通过 <code>authenticator</code> bean 身份验证的用户的业务角色。清单 7 展示 <code>populator</code> bean 的 XML 配置：</p>
<br />
<a name="listing7"><strong>清单 7. populator bean 的 XML 配置</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="populator"
            class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator"&gt;
            &lt;constructor-arg&gt;
            &lt;ref local="initialDirContextFactory"/&gt;
            &lt;/constructor-arg&gt;
            &lt;constructor-arg&gt;
            &lt;value&gt;ou=departments&lt;/value&gt;
            &lt;/constructor-arg&gt;
            &lt;property name="groupRoleAttribute"&gt;
            &lt;value&gt;ou&lt;/value&gt;
            &lt;/property&gt;
            &lt;property name="searchSubtree"&gt;
            &lt;value&gt;true&lt;/value&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>在清单 7 中，<code>populator</code> bean 的构造函数包括 2 个参数，以及一个 <code>groupRoleAttribute</code> 属性。构造函数的第一个参数指定了 <code>populator</code> bean 用来读取经过验证用户的业务角色的初始上下文。并不强制要求 <code>authenticator</code> 和 <code>populator</code> bean 使用相同的初始上下文。您可以为这两者分别配置一个初始上下文。</p>
<p>第二个构造函数参数指定了 populator 前加到初始上下文的 RDN。因此，RDN 组成了包含组用户的节点的 DN，例如 <code>departments</code> 节点。</p>
<p><code>populator</code> bean 的 <code>groupRoleAttribute</code> 属性指定了持有组成员业务角色数据的属性。回想 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#section1.2" cmimpressionsent="1">设置 LDAP 目录</a> 一节中，您将每组用户的业务角色信息存储在名为 <code>ou</code> 的属性中。然后将 <code>ou</code> 设置为 <code>groupRoleAttribute</code> 属性的值，如 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing7" cmimpressionsent="1">清单 7</a> 所示。 </p>
<p>如您所料，<code>populator</code> bean 将搜索整个 LDAP 目录来查找经过验证的用户所属的组节点。然后读取组节点的 <code>ou</code> 属性的值，获取用户经过授权的业务角色。</p>
<p>这样就完成了 <code>populator</code> bean 的配置。目前为止，我们在三个位置使用了初始上下文：<a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing5" cmimpressionsent="1">清单 5</a>、<a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing6" cmimpressionsent="1">清单 6</a> 和 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing7" cmimpressionsent="1">清单 7</a>。接下来将了解如何配置初始上下文。</p>
<p><a name="section5.1"><span class="smalltitle">配置初始上下文</span></a></p>
<p>清单 8 展示了在 Acegi 中配置初始上下文的过程：</p>
<br />
<a name="listing8"><strong>清单 8. 初始上下文的 XML 配置</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="initialDirContextFactory"
            class="org.acegisecurity.ldap.DefaultInitialDirContextFactory"&gt;
            &lt;constructor-arg value="ldap://localhost:389/o=manufacturingEnterprise,dc=org"/&gt;
            &lt;property name="managerDn"&gt;
            &lt;value&gt;cn=manager,o=manufacturingEnterprise,dc=org&lt;/value&gt;
            &lt;/property&gt;
            &lt;property name="managerPassword"&gt;
            &lt;value&gt;secret&lt;/value&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>清单 8 中 Acegi 的初始上下文类的名称为 <code>org.acegisecurity.ldap.DefaultInitialDirContextFactory</code>，这是 Acegi 包含的工厂类。Acegi 在内部使用该类构造其他处理目录操作（例如在整个目录中搜索）的类的对象。当配置初始上下文工厂时，必须指定以下内容： </p>
<ul>
    <li>将您的 LDAP 目录和根目录节点的网络地址指定为构造函数的参数。在初始上下文配置的节点将作为根节点。就是说所有后续操作（例如 <code>search</code>）都将在根节点定义的子树中执行。<br />
    <br />
    <li>将 DN 和密码分别定义为 <code>managerDn</code> 和 <code>managerPassword</code> 属性。在执行任何搜索操作之前，Acegi 必须使用目录服务器对 DN 和密码进行身份验证。 </li>
</ul>
<p>您已经了解了如何将用户库托管在 LDAP 目录中，以及如何配置 Acegi 来使用来自 LDAP 目录的信息对用户进行身份验证。下一节将进一步介绍 Acegi 的身份验证处理过滤器，了解新配置的 bean 是如何管理身份验证过程的。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section6.0"><span class="atitle">身份验证和授权</span></a></p>
<p>APF 配置完成后，将能够与 LDAP 目录进行通信来对用户进行身份验证。如果您阅读过第 1 部分，那么对与目录通信过程中 APF 执行的一些步骤不会感到陌生，我在第 1 部分中向您展示了过滤器如何使用不同的服务进行用户身份验证。图 5 所示的序列表与您在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html#fig3" cmimpressionsent="1">第 1 部分图 3</a> 看到的非常类似：</p>
<br />
<a name="fig5"><strong>图 5. APF 对一名 LDAP 用户进行身份验证</strong></a><br />
<img height="464" alt="图 5. APF 对一名 LDAP 用户进行身份验证" src="http://www.ibm.com/developerworks/cn/java/j-acegi2/figure5.jpg" width="351" /> <br />
<p>无论 APF 使用属性文件进行内部的身份验证还是与 LDAP 服务器进行通信，步骤 1 到步骤 9 与第 1 部分是相同的。这里简单描述了前 9 个步骤，您可以从步骤 10 开始继续学习特定于 LDAP 的事件：</p>
<ol>
    <li>过滤器链前面的过滤器将请求、响应和过滤器链对象传递给 APF。<br />
    <br />
    <li>APF 使用取自请求对象的用户名、密码和其他信息创建一个身份验证标记。<br />
    <br />
    <li>APF 将身份验证标记传递给身份验证管理器。<br />
    <br />
    <li>身份验证管理器可能包含一个或多个身份验证提供者。每个提供者恰好支持一种身份验证类型。管理器将检查哪一种提供者支持从 APF 接收到的身份验证标记。 <br />
    <br />
    <li>身份验证管理器将身份验证标记传递给适合该类型身份验证的提供者。 <br />
    <br />
    <li>身份验证提供者从身份验证标记中提取用户名并将其传递到名为 user cache service 的服务。Acegi 缓存了已经进行过身份验证的用户。该用户下次登录时，Acegi 可以从缓存中加载他或她的详细信息（比如用户名、密码和权限），而不是从后端数据存储中读取数据。这种方法使得性能得到了改善。<br />
    <br />
    <li>user cache service 检查用户的详细信息是否存在于缓存中。<br />
    <br />
    <li>user cache service 将用户的详细信息返回给身份验证提供者。如果缓存不包含用户详细信息，则返回 null。<br />
    <br />
    <li>身份验证提供者检查缓存服务返回的是用户的详细信息还是 null。<br />
    <br />
    <li><strong>从这里开始，身份验证处理将特定于 LDAP。</strong> 如果缓存返回 null，LDAP 身份验证提供者将把用户名（在步骤 6 中提取的）和密码传递给 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing5" cmimpressionsent="1">清单 5</a> 中配置的 <code>authenticator</code> bean。<br />
    <br />
    <li><code>authenticator</code> 将使用在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing5" cmimpressionsent="1">清单 5</a> 的 <code>userDnPatterns</code> 属性中配置的 DN 模式创建用户 DN。通过从一个 DN 模式中创建一个 DN，然后将该 DN 和用户密码（从用户请求中获得）发送到 LDAP 目录，它将逐一尝试所有可用的 DN 模式。LDAP 目录将检查该 DN 是否存在以及密码是否正确。如果其中任何一个 DN 模式可行的话，用户被绑定到 LDAP 目录中，<code>authenticator</code> 将继续执行步骤 15。 <br />
    <br />
    <li>如果任何一种 DN 模式都不能工作的话（这意味着在 DN 模式指定的任何位置都不存在使用给定密码的用户），<code>authenticator</code> 根据 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing6" cmimpressionsent="1">清单 6</a> 配置的搜索查询在 LDAP 目录中搜索用户。如果 LDAP 目录没有找到用户，那么身份验证以失败告终。<br />
    <br />
    <li>如果 LDAP 目录查找到了用户，它将用户的 DN 返回到 <code>authenticator</code>。<br />
    <br />
    <li><code>authenticator</code> 将用户 DN 和密码发送到 LDAP 目录来检查用户密码是否正确。如果 LDAP 目录发现用户密码是正确的，该用户将被绑定到 LDAP 目录。<br />
    <br />
    <li><code>authenticator</code> 将用户信息发送回 LDAP 身份验证提供者。<br />
    <br />
    <li>LDAP 身份验证提供者将控制权传递给 <code>populator</code> bean。<br />
    <br />
    <li><code>populator</code> 搜索用户所属的组。<br />
    <br />
    <li>LDAP 目录将用户角色信息返回给 <code>populator</code>。<br />
    <br />
    <li><code>populator</code> 将用户角色信息返回给 LDAP 身份验证提供者。<br />
    <br />
    <li>LDAP 身份验证提供者将用户的详细信息（以及用户业务角色信息）返回给 APF。用户现在成功进行了身份验证。 </li>
</ol>
<p>不论使用何种身份验证方法，最后三个步骤是相同的（步骤21、21 和 23）。</p>
<p><a name="section6.1"><span class="smalltitle">配置拦截器</span></a></p>
<p>您已经了解了 APF 对用户进行身份验证的步骤。接下来是查看成功进行身份验证的用户是否被授权访问所请求的资源。这项任务由 Acegi 的拦截过滤器（Interceptor Filter，IF）完成。本节将向您展示如何配置 IF 来实现访问控制策略。 </p>
<p>回想一下在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/index.html#listing7" cmimpressionsent="1">第 1 部分的清单 7</a> 中配置 IF 的步骤。拦截过滤器在资源和角色之间建立映射，就是说只有具备必要角色的用户才能访问给定资源。为了演示制造业企业中不同部门的业务角色，清单 9 向现有的 IF 配置添加了另外的角色：</p>
<br />
<a name="listing9"><strong>清单 9. 配置拦截过滤器</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="filterInvocationInterceptor"
            class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;
            &lt;property name="authenticationManager" ref="authenticationManager" /&gt;
            &lt;property name="accessDecisionManager" ref="accessDecisionManager" /&gt;
            &lt;property name="objectDefinitionSource"&gt;
            &lt;value&gt;
            CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
            PATTERN_TYPE_APACHE_ANT
            /protected/engineering/**=ROLE_HEAD_OF_ENGINEERING
            /protected/marketing/**=ROLE_HEAD_OF_MARKETING
            /**=IS_AUTHENTICATED_ANONYMOUSLY
            &lt;/value&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>在清单 9 中，IF 包含三个参数。其中第一个和第三个参数与第 1 部分中最初配置的参数相同。这里添加了第二个参数（名为 <code>accessDecisionManager</code> 的 bean）。</p>
<p><code>accessDecisionManager</code> bean 负责指定授权决策。它使用清单 9 中第三个参数提供的访问控制定义来指定授权（或访问控制）决策。第三个参数是 <code>objectDefinitionSource</code>。</p>
<p><a name="section6.2"><span class="smalltitle">配置访问决策管理器</span></a></p>
<p><code>accessDecisionManager</code> 决定是否允许一个用户访问某个资源。Acegi 提供了一些访问决策管理器，它们指定访问控制决策的方式有所不同。本文只解释了其中一种访问决策管理器的工作方式，其配置如清单 10 所示： </p>
<br />
<a name="listing10"><strong>清单 10. 配置访问决策管理器</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"&gt;
            &lt;property name="decisionVoters"&gt;
            &lt;list&gt;
            &lt;bean class="org.acegisecurity.vote.RoleVoter"/&gt;
            &lt;bean class="org.acegisecurity.vote.AuthenticatedVoter" /&gt;
            &lt;/list&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>在清单 10 中，<code>accessDecisionManager</code> bean 是 <code>org.acegisecurity.vote.AffirmativeBased</code> 类的实例。<code>accessDecisionManager</code> bean 只包含一个参数，即<em>投票者（voter）</em>列表。</p>
<p>在 Acegi 中，投票者确定是否允许某个用户访问特定的资源。当使用 <code>accessDecisionManager</code> 查询时，投票者具有三个选项：允许访问（access-granted）、拒绝访问（access-denied），如果不确定的话则放弃投票（abstain from voting）。</p>
<p>不同类型的访问决策管理器解释投票者决策的方法也有所不同。清单 10 所示的 <code>AffirmativeBased</code> 访问决策管理器实现了简单的决策逻辑：如果任何投票者强制执行肯定投票，将允许用户访问所请求的资源。</p>
<p><a name="section6.3"><span class="smalltitle">投票者逻辑</span></a></p>
<p>Acegi 提供了若干个投票者实现类型。<code>accessDecisionManager</code> 将经过验证的用户的信息（包括用户的业务角色信息）和 <code>objectDefinitionSource</code> 对象传递给投票者。本文的示例使用了两种类型的投票者，<code>RoleVoter</code> 和 <code>AuthenticatedVoter</code>，如清单 10 所示。现在看一下每种投票者的逻辑：</p>
<ul>
    <li><strong>RoleVoter</strong> 只有在 <code>objectDefinitionSource</code> 对象的行中找到以 <code>ROLE_ </code>前缀开头的角色时才进行投票。如果 <code>RoleVoter</code> 没有找到这样的行，将放弃投票；如果在用户业务角色中找到一个匹配的角色，它将投票给允许访问；如果没有找到匹配的角色，则投票给拒绝访问。在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing9" cmimpressionsent="1">清单 9</a> 中，有两个角色具有 <code>ROLE_</code> 前缀：<code>ROLE_HEAD_OF_ENGINEERING</code> 和 <code>ROLE_HEAD_OF_MARKETING</code>。<br />
    <br />
    <li><strong>AuthenticatedVoter</strong> 只有在 <code>objectDefinitionSource</code> 对象中找到具有某个预定义角色的行时才进行投票。在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#listing9" cmimpressionsent="1">清单 9</a> 中，有这样一行：<code>IS_AUTHENTICATED_ANONYMOUSLY</code>。匿名身份验证意味着用户不能够进行身份验证。找到该行后，<code>AuthenticatedVoter</code> 将检查一个匿名身份验证的用户是否可以访问某些不受保护的资源（即这些资源没有包含在具备 <code>ROLE_</code> 前缀的行中）。如果 <code>AuthenticatedVoter</code> 发现所请求的资源是不受保护的并且 <code>objectDefinitionSource</code> 对象允许匿名身份验证的用户访问不受保护的资源，它将投票给允许访问；否则就投票给拒绝访问。 </li>
</ul>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section7.0"><span class="atitle">示例应用程序</span></a></p>
<p>本文提供了一个示例应用程序，它将演示您目前掌握的 LDAP 和 Acegi 概念。LDAP-Acegi 应用程序将显示一个索引页面，该页面将设计和销售文档呈现给合适的经过身份验证的用户。正如您将看到的一样，LDAP-Acegi 应用程序允许用户 <code>alice</code> 查看设计文档，并允许用户 <code>bob</code> 查看销售文档。它还允许特定用户同时查看设计和销售文档。所有这些内容都是在本文开头配置 LDAP 目录服务器时设置的。立即 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#download" cmimpressionsent="1">下载示例应用程序</a> 来开始使用它。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi2/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section8.0"><span class="atitle">结束语</span></a></p>
<p>在本文中，您了解了如何将用户和业务角色信息托管在 LDAP 目录中。您还详细了解了配置 Acegi 的方法，从而与 LDAP 目录交互实现访问控制策略。在本系列最后一期文章中，我将展示如何配置 Acegi 来保护对 Java 类的访问。</p>
<br />
<br />
来自:http://www.cnblogs.com/amboyna/archive/2008/03/25/1122084.html
<img src ="http://www.blogjava.net/waterjava/aggbug/199311.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/waterjava/" target="_blank">狼爱上狸</a> 2008-05-08 18:43 <a href="http://www.blogjava.net/waterjava/archive/2008/05/08/199311.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>acegi,IBM的Acegi Security System（1） </title><link>http://www.blogjava.net/waterjava/archive/2008/05/08/199309.html</link><dc:creator>狼爱上狸</dc:creator><author>狼爱上狸</author><pubDate>Thu, 08 May 2008 10:41:00 GMT</pubDate><guid>http://www.blogjava.net/waterjava/archive/2008/05/08/199309.html</guid><wfw:comment>http://www.blogjava.net/waterjava/comments/199309.html</wfw:comment><comments>http://www.blogjava.net/waterjava/archive/2008/05/08/199309.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/waterjava/comments/commentRss/199309.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/waterjava/services/trackbacks/199309.html</trackback:ping><description><![CDATA[<p>2007 年 5 月 08 日</p>
<blockquote>这份共分三部分的系列文章介绍了 Acegi 安全系统（Acegi Security System），它是用于 Java&#8482; 企业应用程序的强大的开源安全框架。在第一篇文章中，Bilal Siddiqui 顾问将向您介绍 Acegi 的架构和组件，并展示如何使用它来保护一个简单的 Java 企业应用程序。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p>Acegi Security System 是一种功能强大并易于使用的替代性方案，使您不必再为 Java 企业应用程序编写大量的安全代码。虽然它专门针对使用 Spring 框架编写的应用程序，但是任何类型的 Java 应用程序都没有理由不去使用 Acegi。这份共分三部分的系列文章详细介绍了 Acegi，并展示了如何使用它保护简单的企业应用程序以及更复杂的应用程序。</p>
<p>本系列首先介绍企业应用程序中常见的安全问题，并说明 Acegi 如何解决这些问题。您将了解 Acegi 的架构模型及其安全过滤器，后者包含了在保护应用程序中将用到的大多数功能。您还将了解到各个过滤器如何单独进行工作，如何将它们组合起来，以及过滤器如何在一个企业安全实现中将各种功能从头到尾地链接起来。本文最后通过一个样例应用程序演示了基于 URL 安全系统的 Acegi 实现。本系列后续两篇文章将探究 Acegi 的一些更高级的应用，包括如何设计和托管访问控制策略，然后如何去配置 Acegi 以使用这些策略。</p>
<p>您必须 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#section6.2" cmimpressionsent="1">下载 Acegi</a>，这样才能编译本文的示例代码并运行本文的样例应用程序。还必须有作为工作站的一部分运行的 Tomcat 服务器。</p>
<p><a name="section1"><span class="atitle">企业应用程序安全性</span></a></p>
<p>由于企业内容管理（ECM）应用程序管理存储在不同类型数据源（如文件系统、关系数据库和目录服务）中的企业内容的编写和处理，ECM 安全性要求对这些数据源的访问进行控制。比如，一个 ECM 应用程序可能会控制被授权读取、编辑或删除数据的对象，而这些数据和制造业企业的设计、市场营销、生产以及质量控制有关。</p>
<p>在一个 ECM 安全场景中，比较常见的就是通过对企业资源定位符（或网络地址）应用安全性，从而实现访问控制。这种简单的安全模型被称为<em>统一资源定位符</em> 或 URL 安全性。正如我在本文后面（以及本系列后续文章）所演示的一样，Acegi 为实现 URL 安全性提供了全面的特性。</p>
<p>然而，在很多企业场景中，URL 安全性还远远不够。比如，假设一个 PDF 文档包含某个制造业公司生产的特殊产品的数据。文档的一部分包含了将由该公司的设计部门编辑和更新的设计数据。另一部分包含了生产经理将使用的生产数据。对于诸如此类的场景，需要实现更加细粒度的安全性，对文档的不同部分应用不同的访问权限。</p>
<p>本文介绍了 Acegi 为实现 URL 安全性而提供的各种功能。本系列的下一篇文章将演示此框架的基于方法的安全性，它提供了对企业数据访问的更细粒度的控制。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section2"><span class="atitle">Acegi Security System</span></a></p>
<p>Acegi Security System 使用安全过滤器来提供企业应用程序的身份验证和授权服务。该框架提供了不同类型的过滤器，可以根据应用程序的需求进行配置。您将在本文后面了解到 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#section4" cmimpressionsent="1">安全过滤器的不同类型</a>；现在，只需注意可以为如下任务配置 Acegi 安全过滤器：</p>
<ol>
    <li>在访问一个安全资源之前提示用户登录。<br />
    <br />
    <li>通过检查安全标记（如密码），对用户进行身份验证。<br />
    <br />
    <li>检查经过身份验证的用户是否具有访问某个安全资源的特权。<br />
    <br />
    <li>将成功进行身份验证和授权的用户重定向到所请求的安全资源。<br />
    <br />
    <li>对不具备访问安全资源特权的用户显示 Access Denied 页面。<br />
    <br />
    <li>在服务器上记录成功进行身份验证的用户，并在用户的客户机上设置安全 cookie。使用该 cookie 执行下一次身份验证，而无需要求用户登录。<br />
    <br />
    <li>将身份验证信息存储在服务器端的会话对象中，从而安全地进行对资源的后续请求。<br />
    <br />
    <li>在服务器端对象中构建并保存安全信息的缓存，从而优化性能。<br />
    <br />
    <li>当用户退出时，删除为用户安全会话而保存的服务器端对象。<br />
    <br />
    <li>与大量后端数据存储服务（如目录服务或关系数据库）进行通信，这些服务用于存储用户的安全信息和 ECM 的访问控制策略。 </li>
</ol>
<p>正如这个列表显示的那样，Acegi 的安全过滤器允许您执行保护企业应用程序所需的几乎任何事情。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section3"><span class="atitle">架构和组件</span></a></p>
<p>对 Acegi 了解越多，使用起来就越简单。这一节介绍 Acegi 的组件；接下来您将了解该框架如何使用反转控制（IOC）和 XML 配置文件来组合组件并表示它们的依赖关系。 </p>
<p><a name="section3.1"><span class="smalltitle">四大组件</span></a></p>
<p>Acegi Security System 由四种主要类型的组件组成：过滤器、管理器、提供者和处理程序。</p>
<dl>
<dt><strong>过滤器</strong>
<dd>这种最高级的组件提供了常见的安全服务，如身份验证、会话处理以及注销。我将在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#section4" cmimpressionsent="1">本文后面的部分</a> 深入讨论过滤器。
<dt><strong>管理器</strong>
<dd>过滤器仅是安全相关功能的高级抽象：实际上要使用管理器和提供者实现身份验证处理和注销服务。管理器管理由不同提供者提供的较低级的安全服务。
<dt><strong>提供者</strong>
<dd>有大量的提供者可用于和不同类型的数据存储服务通信，例如目录服务、关系数据库或简单的内存中的对象。这意味着您可以将用户库和访问控制协议存储在任何一种这样的数据存储服务中，并且 Acegi 的管理器将在运行时选择合适的提供者。
<dt><strong>处理程序</strong>
<dd>有时任务可能会被分解为多个步骤，其中每个步骤由一个特定的处理程序执行。比方说，Acegi 的 <em>注销过滤器</em> 使用两个处理程序来退出一个 HTTP 客户机。其中一个处理程序使用户的 HTTP 会话无效，而另一个处理程序则删除用户的 cookie。当根据应用程序需求配置 Acegi 时，多个处理程序能够提供很好的灵活性。您可以使用自己选择的处理程序来执行保护应用程序所需的步骤。 </dd></dl>
<p><a name="section3.2"><span class="smalltitle">反转控制</span></a></p>
<p>Acegi 的组件通过彼此之间的依赖来对企业应用程序进行保护。比如，一个身份验证处理过滤器需要一个身份验证管理器选择一个合适的身份验证提供者。这就是说您必须能够表示和管理 Acegi 组件的依赖关系。 </p>
<p>IOC 实现通常用于管理 Java 组件之间的依赖关系。IOC 提供了两个重要的特性：</p>
<ol>
    <li>它提供了一种语法，表示应用程序所需的组件以及这些组件如何相互依赖。<br />
    <br />
    <li>它保证了所需的组件在运行时是可用的。 </li>
</ol>
<p><a name="section3.3"><span class="smalltitle">XML 配置文件</span></a></p>
<p>Acegi 使用 Spring 框架（请参见 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#resources" cmimpressionsent="1">参考资料</a>）附带的流行开源 IOC 实现来管理其组件。Spring 需要您编写一个 XML 配置文件来表示组件的依赖关系，如清单 1 所示：</p>
<br />
<a name="listing1"><strong>清单 1. Spring 配置文件的结构</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;beans&gt;
            &lt;bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"&gt;
            &lt;property name="filterInvocationDefinitionSource"&gt;
            &lt;value&gt; value here &lt;/value&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            &lt;bean id="authenticationProcessingFilter"
            class="org.acegisecurity.ui.webapp.AuthenticationProcessingFitler"&gt;
            &lt;property name="authenticationManager" ref="authManager"/&gt;
            &lt;!-- Other properties --&gt;
            &lt;/bean&gt;
            &lt;bean id="authManager"
            class="org.acegisecurity.providers.ProviderManager"&gt;
            &lt;property name="providers"&gt;
            &lt;!--  List of providers here --&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            &lt;!-- Other bean tags --&gt;
            &lt;/beans&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>如您所见，Acegi 使用的 Spring XML 配置文件包含一个 <code>&lt;beans&gt;</code> 标记，它封装了一些其他的 <code>&lt;bean&gt;</code> 标记。所有的 Acegi 组件（即过滤器、管理器、提供者等）实际上都是 JavaBean。XML 配置文件中的每个 <code>&lt;bean&gt;</code> 标记都代表一个 Acegi 组件。</p>
<p><strong>进一步解释 XML 配置文件</strong> <br />
<br />
首先将注意到的是每个 <code>&lt;bean&gt;</code> 标记都包含一个 <em>class</em> 属性，这个属性标识组件所使用的类。<code>&lt;bean&gt;</code> 标记还具有一个 <em>id</em> 属性，它标识作为 Acegi 组件工作的实例（Java 对象）。</p>
<p>比方说，<a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#listing1" cmimpressionsent="1">清单 1</a> 的第一个 <code>&lt;bean&gt;</code> 标记标识了名为 <code>filterChainProxy</code> 的组件实例，它是名为 <code>org.acegisecurity.util.FilterChainProxy</code> 的类的实例。</p>
<p>使用 <code>&lt;bean&gt;</code> 标记的子标记来表示 bean 的依赖关系。比如，注意第一个 <code>&lt;bean&gt;</code> 标记的 <code>&lt;property&gt;</code> 子标记。<code>&lt;property&gt;</code> 子标记定义了 <code>&lt;bean&gt;</code> 标记依赖的其他 bean 或值。</p>
<p>所以在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#listing1" cmimpressionsent="1">清单 1</a> 中，第一个 <code>&lt;bean&gt;</code> 标记的 <code>&lt;property&gt;</code> 子标记具有一个 name 属性和一个 <code>&lt;value&gt;</code> 子标记，分别定义了这个 bean 依赖的属性的名称和值。</p>
<p>同样，<a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#listing1" cmimpressionsent="1">清单 1</a> 中的第二个和第三个 <code>&lt;bean&gt;</code> 标记定义了一个过滤器 bean 依赖于一个管理器 bean。第二个 <code>&lt;bean&gt;</code> 标记表示过滤器 bean，而第三个 <code>&lt;bean&gt;</code> 标记表示管理器 bean。</p>
<p>过滤器的 <code>&lt;bean&gt;</code> 标记包含一个 <code>&lt;property&gt;</code> 子标记，该子标记具有两个属性：<code>name</code> 和 <code>ref</code>。<code>name</code> 属性定义了过滤器 bean 的属性，而 <code>ref</code> 属性引用了管理器 bean 的实例（名称）。</p>
<p>下一节将展示如何在 XML 配置文件中配置 Acegi 过滤器。在本文后面的内容中，您将在一个样例 Acegi 应用程序中使用过滤器。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section4"><span class="atitle">安全过滤器</span></a></p>
<p>正如我前面提到的一样，Acegi 使用安全过滤器为企业应用程序提供身份验证和授权服务。您可以根据应用程序的需要使用和配置不同类型的过滤器。这一节将介绍五种最重要的 Acegi 安全过滤器。</p>
<p><a name="section4.1"><span class="smalltitle">Session Integration Filter</span></a></p>
<p>Acegi 的 <em>Session Integration Filter</em>（SIF）通常是您将要配置的第一个过滤器。SIF 创建了一个<em>安全上下文对象</em>，这是一个与安全相关的信息的占位符。其他 Acegi 过滤器将安全信息保存在安全上下文中，也会使用安全上下文中可用的安全信息。</p>
<p>SIF 创建安全上下文并调用过滤器链中的其他过滤器。然后其他过滤器检索安全上下文并对其进行更改。比如，Authentication Processing Filter（我将稍后进行介绍）将用户信息（如用户名、密码和电子邮件地址）存储在安全上下文中。 </p>
<p>当所有的处理程序完成处理后，SIF 检查安全上下文是否更新。如果任何一个过滤器对安全上下文做出了更改，SIF 将把更改保存到服务器端的会话对象中。如果安全上下文中没有发现任何更改，那么 SIF 将删除它。</p>
<p>在 XML 配置文件中对 SIF 进行了配置，如清单 2 所示：</p>
<br />
<a name="listing2"><strong>清单 2. 配置 SIF</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="httpSessionContextIntegrationFilter"
            class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><a name="section4.2"><span class="smalltitle">Authentication Processing Filter</span></a></p>
<p>Acegi 使用 <em>Authentication Processing Filter</em>（APF）进行身份验证。APF 使用一个身份验证（或登录）表单，用户在其中输入用户名和密码，并触发身份验证。</p>
<p>APF 执行所有的后端身份验证处理任务，比如从客户机请求中提取用户名和密码，从后端用户库中读取用户参数，以及使用这些信息对用户进行身份验证。 </p>
<p>在配置 APF 时，您必须提供如下参数：</p>
<ul>
    <li><strong>Authentication manager</strong> 指定了用来管理身份验证提供者的身份验证管理器。<br />
    <br />
    <li><strong>Filter processes URL</strong> 指定了客户在登录窗口中按下 <strong>Sign In</strong> 按钮时要访问的 URL。收到这个 URL 的请求后，Acegi 立即调用 APF。<br />
    <br />
    <li><strong>Default target URL</strong> 指定了成功进行身份验证和授权后呈现给用户的页面。<br />
    <br />
    <li><strong>Authentication failure URL</strong> 指定了身份验证失败情况下用户看到的页面。 </li>
</ul>
<p>APF 从用户的请求对象中得到用户名、密码和其他信息。它将这些信息传送给身份验证管理器。身份验证管理器使用适当的提供者从后端用户库中读取详细的用户信息（如用户名、密码、电子邮件地址和用户访问权利或特权），对用户进行身份验证，并将信息存储在一个 <code>Authentication</code> 对象中。</p>
<p>最后，APF 将 <code>Authentication</code> 对象保存在 SIF 之前创建的安全上下文中。存储在安全上下文中的 <code>Authentication</code> 对象将用于做出授权决策。</p>
<p>APF 的配置如清单 3 所示：</p>
<br />
<a name="listing3"><strong>清单 3. 配置 APF</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="authenticationProcessingFilter"
            class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"&gt;
            &lt;property name="authenticationManager"
            ref="authenticationManager" /&gt;
            &lt;property name="filterProcessesUrl"
            value="/j_acegi_security_check" /&gt;
            &lt;property name="defaultTargetUrl"
            value="/protected/protected1.jsp" /&gt;
            &lt;property name="authenticationFailureUrl"
            value="/login.jsp?login_error=1" /&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>可以从这段代码中看到，APF 依赖于上面讨论的这四个参数。每个参数都是作为清单 3 所示的 <code>&lt;property&gt;</code> 标记配置的。</p>
<p><a name="section4.3"><span class="smalltitle">Logout Processing Filter</span></a></p>
<p>Acegi 使用一个 <em>Logout Processing Filer</em>（LPF）管理注销处理。当客户机发来注销请求时，将使用 LPF 进行处理。它标识了来自由客户机所调用 URL 的注销请求。</p>
<p>LPF 的配置如清单 4 所示：</p>
<br />
<a name="listing4"><strong>清单 4. 配置 LPF</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter"&gt;
            &lt;constructor-arg value="/logoutSuccess.jsp"/&gt;
            &lt;constructor-arg&gt;
            &lt;list&gt;
            &lt;bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/&gt;
            &lt;/list&gt;
            &lt;/constructor-arg&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>可以看到 LPF 在其构造方法中包含两个参数：注销成功 URL（<code>/logoutSuccess.jsp</code>）和处理程序列表。注销成功 URL 用来在注销过程完成后重定向客户机。处理程序执行实际的注销过程；我在这里只配置了一个处理程序，因为只需一个处理程序就可以使 HTTP 会话变为无效。我将在本系列下一篇文章中讨论更多的处理程序。</p>
<p><a name="section4.4"><span class="smalltitle">Exception Translation Filter</span></a></p>
<p><em>Exception Translation Filter</em>（ETF）处理身份验证和授权过程中的异常情况，比如授权失败。在这些异常情况中，ETF 将决定如何进行操作。</p>
<p>比如，如果一个没有进行身份验证的用户试图访问受保护的资源，ETF 将显示一个登录页面要求用户进行身份验证。类似地，在授权失败的情况下，可以配置 ETF 来呈现一个 Access Denied 页面。</p>
<p>ETF 的配置如清单 5 所示：</p>
<br />
<a name="listing5"><strong>清单 5. 配置 ETF</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="exceptionTranslationFilter"
            class="org.acegisecurity.ui.ExceptionTranslationFilter"&gt;
            &lt;property name="authenticationEntryPoint"&gt;
            &lt;bean
            class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"&gt;
            &lt;property name="loginFormUrl" value="/login.jsp" /&gt;
            &lt;/bean&gt;
            &lt;/property&gt;
            &lt;property name="accessDeniedHandler"&gt;
            &lt;bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl"&gt;
            &lt;property name="errorPage" value="/accessDenied.jsp" /&gt;
            &lt;/bean&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>正如清单 5 所示，ETF 包含两个参数，名为 <code>authenticationEntryPoint</code> 和 <code>accessDeniedHandler</code>。<code>authenticationEntryPoint</code> 属性指定登录页面，而 <code>accessDeniedHandler</code> 指定 Access Denied 页面。</p>
<p><a name="section4.5"><span class="smalltitle">拦截过滤器</span></a></p>
<p>Acegi 的<em>拦截过滤器</em> 用于做出授权决策。您需要在 APF 成功执行身份验证后对拦截过滤器进行配置，以使其发挥作用。拦截器使用应用程序的访问控制策略来做出授权决定。</p>
<p>本系列的下一篇文章将展示如何设计访问控制策略，如何将它们托管在目录服务中，以及如何配置 Acegi 以读取您的访问控制策略。但是，目前我将继续向您展示如何使用 Acegi 配置一个简单的访问控制策略。在本文后面的部分，您将看到使用简单的访问控制策略构建一个样例应用程序。</p>
<p>配置简单的访问控制策略可分为两个步骤：</p>
<ol>
    <li>编写访问控制策略。<br />
    <br />
    <li>根据策略配置 Acegi 的拦截过滤器。 </li>
</ol>
<p><strong>步骤 1. 编写简单的访问控制策略</strong> <br />
<br />
首先看一下 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#listing6" cmimpressionsent="1">清单 6</a>，它展示了如何定义一个用户及其用户角色：</p>
<br />
<a name="listing6"><strong>清单 6. 为用户定义简单的访问控制策略</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        alice=123,ROLE_HEAD_OF_ENGINEERING
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>清单 6 所示的访问控制策略定义了用户名 <code>alice</code>，它的密码是 <code>123</code>，角色是 <code>ROLE_HEAD_OF_ENGINEERING</code>。（下一节将说明如何在文件中定义任意数量的用户及其用户角色，然后配置拦截过滤器以使用这些文件。）</p>
<p><strong>步骤 2. 配置 Acegi 的拦截过滤器</strong> <br />
<br />
拦截过滤器使用三个组件来做出授权决策，我在清单 7 中对其进行了配置：</p>
<br />
<a name="listing7"><strong>清单 7. 配置拦截过滤器 </strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;bean id="filterInvocationInterceptor"
            class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;
            &lt;property name="authenticationManager" ref="authenticationManager" /&gt;
            &lt;property name="accessDecisionManager" ref="accessDecisionManager" /&gt;
            &lt;property name="objectDefinitionSource"&gt;
            &lt;value&gt;
            CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
            PATTERN_TYPE_APACHE_ANT
            /protected/**=ROLE_HEAD_OF_ENGINEERING
            /**=IS_AUTHENTICATED_ANONYMOUSLY
            &lt;/value&gt;
            &lt;/property&gt;
            &lt;!--  More properties of the interceptor filter --&gt;
            &lt;/bean&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>如清单 7 所示，配置所需的三个组件是 <code>authenticationManager</code>、<code>accessDecisionManager</code>、<code>objectDefinitionSource</code>：</p>
<ul>
    <li><code>authenticationManager</code> 组件与我在介绍 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#section4.2" cmimpressionsent="1">Authentication Processing Filter</a> 时讨论过的身份验证管理器相同。拦截过滤器可以在授权的过程中使用 <code>authenticationManager</code> 重新对客户机进行身份验证。<br />
    <br />
    <li><code>accessDecisionManager</code> 组件管理授权过程，这部分内容将在本系列的下篇文章中详细讨论。<br />
    <br />
    <li><code>objectDefinitionSource</code> 组件包含对应于将要发生的授权的访问控制定义。例如，<a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#listing7" cmimpressionsent="1">清单 7</a> 中的 <code>objectDefinitionSource</code> 属性值包含两个 URL（<code>/protected/*</code> 和 <code>/*</code>）。其值定义了这些 URL 的角色。<code>/protected/*</code> URL 的角色是 <code>ROLE_HEAD_OF_ENGINEERING</code>。您可以根据应用程序的需要定义任何角色。<br />
    <br />
    回想一下 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#listing6" cmimpressionsent="1">清单 6</a>，您为用户名 <code>alice</code> 定义了 <code>ROLE_HEAD_OF_ENGINEERING</code>。这就是说 <code>alice</code> 将能够访问 <code>/protected/*</code> URL。 </li>
</ul>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section5"><span class="atitle">过滤器工作原理</span></a></p>
<p>正如您已经了解到的一样，Acegi 的组件彼此依赖，从而对您的应用程序进行保护。在本文后面的部分，您将看到如何对 Acegi 进行配置，从而按照特定的顺序应用安全过滤器，因此需要创建<em>过滤器链</em>。出于这个目的，Acegi 保存了一个过滤器链对象，它封装了为保护应用程序而配置了的所有过滤器。图 1 展示了 Acegi 过滤器链的生命周期，该周期从客户机向您的应用程序发送 HTTP 请求开始。（图 1 显示了服务于浏览器客户机的容器。）</p>
<br />
<a name="fig1"><strong>图 1. 托管 Acegi 过滤器链以安全地为浏览器客户机服务的容器</strong></a><br />
<img height="187" alt="Acegi 过滤器的生命周期" src="http://www.ibm.com/developerworks/cn/java/j-acegi1/figure1.gif" width="315" /> <br />
<p>下面的步骤描述了过滤器链的生命周期：</p>
<ol>
    <li>浏览器客户机向您的应用程序发送 HTTP 请求。<br />
    <br />
    <li>容器接收到 HTTP 请求并创建一个请求对象，该对象将封装 HTTP 请求中包含的信息。容器还创建一个各种过滤器都可处理的响应对象，从而为发出请求的客户机准备好 HTTP 响应。容器然后调用 Acegi 的过滤器链代理，这是一个代理过滤器。该代理知道应用的过滤器的实际顺序。当容器调用代理时，它将向代理发送请求、响应以及过滤器链对象。<br />
    <br />
    <li>代理过滤器调用过滤器链中第一个过滤器，向其发送请求、响应和过滤器链对象。<br />
    <br />
    <li>链中的过滤器逐个执行其处理。一个过滤器可以通过调用过滤器链中下一个过滤器随时终止自身处理。有的过滤器甚至根本不执行任何处理（比如，如果 APF 发现一个到来的请求没有要求身份验证，它可能会立即终止其处理）。 <br />
    <br />
    <li>当身份验证过滤器完成其处理时，这些过滤器将把请求和响应对象发送到应用程序中配置的拦截过滤器。<br />
    <br />
    <li>拦截器决定是否对发出请求的客户机进行授权，使它访问所请求的资源。<br />
    <br />
    <li>拦截器将控制权传输给应用程序（比如，成功进行了身份验证和授权的客户机请求的 JSP 页面）。<br />
    <br />
    <li>应用程序改写响应对象的内容。<br />
    <br />
    <li>响应对象已经准备好了，容器将响应对象转换为 HTTP 响应，并将响应发送到发出请求的客户机。<br />
    <br />
    </li>
</ol>
<p>为帮助您进一步理解 Acegi 过滤器，我将详细探讨其中两个过滤器的操作：Session Integration Filter 和 Authentication Processing Filter。</p>
<p><a name="section5.1"><span class="smalltitle">SIF 如何创建一个安全上下文</span></a></p>
<p>图 2 展示了 SIF 创建安全上下文所涉及到的步骤：</p>
<br />
<a name="fig2"><strong>图 2. SIF 创建安全上下文</strong></a><br />
<img height="262" alt="SIF 如何创建安全上下文" src="http://www.ibm.com/developerworks/cn/java/j-acegi1/figure2.gif" width="317" /> <br />
<p>现在详细地考虑下面这些步骤：</p>
<ol>
    <li>Acegi 的过滤器链代理调用 SIF 并向其发送请求、响应和过滤器链对象。注意：通常将 SIF 配置为过滤器链中第一个过滤器。<br />
    <br />
    <li>SIF 检查它是否已经对这个 Web 请求进行过处理。如果是的话，它将不再进一步进行处理，并将控制权传输给过滤器链中的下一个过滤器（参见下面的第 4 个步骤）。如果 SIF 发现这是第一次对这个 Web 请求调用 SIF，它将设置一个标记，将在下一次使用该标记，以表示曾经调用过 SIF。<br />
    <br />
    <li>SIF 将检查是否存在一个会话对象，以及它是否包含安全上下文。它从会话对象中检索安全上下文，并将其放置在名为 <em>security context holder</em> 的临时占位符中。如果不存在会话对象，SIF 将创建一个新的安全上下文，并将它放到 security context holder 中。注意：security context holder 位于应用程序的范围内，所以可以被其他的安全过滤器访问。<br />
    <br />
    <li>SIF 调用过滤器链中的下一个过滤器。<br />
    <br />
    <li>其他过滤器可以编辑安全上下文。<br />
    <br />
    <li>SIF 在过滤器链完成处理后接收控制权。<br />
    <br />
    <li>SIF 检查其他的过滤器是否在其处理过程中更改了安全上下文（比如，APF 可能将用户详细信息存储在安全上下文中）。如果是的话，它将更新会话对象中的安全上下文。就是说在过滤器链处理过程中，对安全上下文的任何更改现在都保存在会话对象中。 </li>
</ol>
<p><a name="section5.2"><span class="smalltitle">APF 如何对用户进行身份验证</span></a></p>
<p>图 3 展示了 APF 对用户进行身份验证所涉及到的步骤：</p>
<br />
<a name="fig3"><strong>图 3. APF 对用户进行身份验证</strong></a><br />
<img height="340" alt="APF 如何对用户进行身份验证" src="http://www.ibm.com/developerworks/cn/java/j-acegi1/figure3.gif" width="392" /> <br />
<p>现在仔细考虑以下这些步骤：</p>
<ol>
    <li>过滤器链中前面的过滤器向 APF 发送请求、响应和过滤链对象。<br />
    <br />
    <li>APF 使用从请求对象中获得的用户名、密码以及其他信息创建身份验证标记。<br />
    <br />
    <li>APF 将身份验证标记传递给身份验证管理器。<br />
    <br />
    <li>身份验证管理器可能包含一个或更多身份验证提供者。每个提供者恰好支持一种类型的身份验证。管理器检查哪一种提供者支持它从 APF 收到的身份验证标记。<br />
    <br />
    <li>身份验证管理器将身份验证标记发送到适合进行身份验证的提供者。<br />
    <br />
    <li>身份验证提供者支持从身份验证标记中提取用户名，并将它发送给名为 <em>user cache service</em> 的服务。Acegi 缓存了已经进行过身份验证的用户。该用户下次登录时，Acegi 可以从缓存中加载他或她的详细信息（比如用户名、密码和权限），而不是从后端数据存储中读取数据。这种方法使得性能得到了改善。<br />
    <br />
    <li>user cache service 检查用户的详细信息是否存在于缓存中。<br />
    <br />
    <li>user cache service 将用户的详细信息返回给身份验证提供者。如果缓存不包含用户详细信息，则返回 null。<br />
    <br />
    <li>身份验证提供者检查缓存服务返回的是用户的详细信息还是 null。<br />
    <br />
    <li>如果缓存返回 null，身份验证提供者将用户名（在步骤 6 中提取）发送给另一个名为 <em>user details service</em> 的服务。 <br />
    <br />
    <li>user details service 与包含用户详细信息的后端数据存储通信（如目录服务）。 <br />
    <br />
    <li>user details service 返回用户的详细信息，或者，如果找不到用户详细信息则抛出身份验证异常。<br />
    <br />
    <li>如果 user cache service 或者 user details service 返回有效的用户详细信息，身份验证提供者将使用 user cache service 或 user details service 返回的密码来匹配用户提供的安全标记（如密码）。如果找到一个匹配，身份验证提供者将用户的详细信息返回给身份验证管理器。否则的话，则抛出一个身份验证异常。<br />
    <br />
    <li>身份验证管理器将用户的详细信息返回给 APF。这样用户就成功地进行了身份验证。<br />
    <br />
    <li>APF 将用户详细信息保存在 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#fig2" cmimpressionsent="1">图 2</a> 所示由步骤 3 创建的安全上下文中。<br />
    <br />
    <li>APF 将控制权传输给过滤器链中的下一个过滤器。 </li>
</ol>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section6"><span class="atitle">一个简单的 Acegi 应用程序</span></a></p>
<p>在本文中，您已经了解了很多关于 Acegi 的知识，所以现在看一下利用您目前学到的知识能做些什么，从而结束本文。对于这个简单的演示，我设计了一个样例应用程序（参见 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#download" cmimpressionsent="1">下载</a>），并对 Acegi 进行了配置以保护它的一些资源。 </p>
<p>样例应用程序包含 5 个 JSP 页面：index.jsp、protected1.jsp、protected2.jsp、login.jsp 和 accessDenied.jsp。</p>
<p>index.jsp 是应用程序的欢迎页面。它向用户显示了三个超链接，如图 4 所示：</p>
<br />
<a name="fig4"><strong>图 4. 样例应用程序的欢迎页面：</strong></a><br />
<img height="280" alt="一个简单的 Acegi 应用程序" src="http://www.ibm.com/developerworks/cn/java/j-acegi1/figure4.jpg" width="468" /> <br />
<p>图 4 所示的链接中，其中两个链接指向了被保护的资源（protected1.jsp 和 protected2.jsp），第三个链接指向登录页面（login.jsp）。只有在 Acegi 发现用户没有被授权访问受保护的资源时，才会显示 accessDenied.jsp 页面。</p>
<p>如果用户试图访问任何受保护的页面，样例应用程序将显示登录页面。当用户使用登录页面进入后，应用程序将自动重定向到被请求的受保护资源。</p>
<p>用户可以通过单击欢迎页面中的第三个链接直接请求登录页面。这种情况下，应用程序显示用户可以进入系统的登录页面。进入系统以后，应用程序将用户重定向到 protected1.jsp，它是用户进入系统而没有请求特定的受保护资源时显示的默认资源。</p>
<p><a name="section6.1"><span class="smalltitle">配置样例应用程序</span></a></p>
<p>为本文下载的源代码包含一个名为 acegi-config.xml 的 XML 配置文件，它包含 Acegi 过滤器的配置。根据 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#section4" cmimpressionsent="1">安全过滤器的讨论</a> 中的示例，您应该很熟悉这些配置。</p>
<p>我还为样例应用程序编写了一个 <code>web.xml</code> 文件，如清单 8 所示：</p>
<br />
<a name="listing8"><strong>清单 8. 样例应用程序的 web.xml 文件</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                        &lt;web-app&gt;
            &lt;context-param&gt;
            &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
            &lt;param-value&gt;/WEB-INF/acegi-config.xml&lt;/param-value&gt;
            &lt;/context-param&gt;
            &lt;filter&gt;
            &lt;filter-name&gt;Acegi Filter Chain Proxy&lt;/filter-name&gt;
            &lt;filter-class&gt;
            org.acegisecurity.util.FilterToBeanProxy
            &lt;/filter-class&gt;
            &lt;init-param&gt;
            &lt;param-name&gt;targetClass&lt;/param-name&gt;
            &lt;param-value&gt;
            org.acegisecurity.util.FilterChainProxy
            &lt;/param-value&gt;
            &lt;/init-param&gt;
            &lt;/filter&gt;
            &lt;filter-mapping&gt;
            &lt;filter-name&gt;Acegi Filter Chain Proxy&lt;/filter-name&gt;
            &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
            &lt;/filter-mapping&gt;
            &lt;listener&gt;
            &lt;listener-class&gt;
            org.springframework.web.context.ContextLoaderListener
            &lt;/listener-class&gt;
            &lt;/listener&gt;
            &lt;/web-app&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>web.xml 文件配置如下：</p>
<ul>
    <li><code>acegi-config.xml</code> 文件的 URL 位于 <code>&lt;context-param&gt;</code> 标记中。<br />
    <br />
    <li>Acegi 过滤器链代理类的名称位于 <code>&lt;filter&gt;</code> 标记中。<br />
    <br />
    <li>URL 到 Acegi 过滤器链代理的映射在 <code>&lt;filter-mapping&gt;</code> 标记中。注意：您可以简单地将应用程序的所有 URL（<code>/*</code>）映射到 Acegi 过滤器链代理。Acegi 将对映射到 Acegi 过滤器链代理上的所有 URL 应用安全性。<br />
    <br />
    <li>应用程序上下文加载程序位于 <code>&lt;listener&gt;</code> 标记中，它将加载 Spring 的 IOC 框架。 </li>
</ul>
<p><a name="section6.2"><span class="smalltitle">部署并运行应用程序</span></a></p>
<p>部署并运行样例应用程序非常的简单。只需要完成两件事情：</p>
<ol>
    <li>将 acegisample.war 文件从本教程下载的源代码中复制到安装 Tomcat 的 <code>webapps</code> 目录中。<br />
    <br />
    <li>从 Acegi Security System 主页 <a href="http://www.acegisecurity.org/downloads.html" cmimpressionsent="1">下载并解压缩 acegi-security-1.0.3.zip</a>。您将找到一个名为 acegi-security-sample-tutorial.war 的样例应用程序。解压缩 war 文件并提取其 WEB-INF/lib 文件夹中所有的 jar 文件。将所有的 JAR 文件从 WEB-INF/lib 文件夹中复制到 theacegisample.war 应用程序的 WEB-INF/lib 文件夹。 </li>
</ol>
<p>现在，您已经为运行样例应用程序做好准备了。启动 Tomcat 并将浏览器指向 <code>http://localhost:8080/acegisample/</code>。</p>
<p>您将看到 <a href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#fig4" cmimpressionsent="1">图 4</a> 所示的欢迎页面，但是此时显示的页面是真实的。请继续运行程序，并查看在尝试访问欢迎页面显示的不同链接时会发生什么状况。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="section7"><span class="atitle">结束语</span></a></p>
<p>在<em>使用 Acegi 保护 Java 应用程序</em> 系列的第一篇文章中，您了解了 Acegi 安全系统的特性、架构和组件，学习了大量有关 Acegi 安全过滤器的知识，这些过滤器被集成到 Acegi 的安全框架中。您还学习了如何使用 XML 配置文件配置组件依赖关系，并查看了 Acegi 的安全过滤器在样例程序中工作的情形，该应用程序可以实现基于 URL 的安全性。 </p>
<p>本文所述的安全技术非常的简单，所以 Acegi 使用这些技术实现安全性。本系列的下一文章将开始介绍 Acegi 的一些较为高级的应用，首先是编写访问控制协议并将其存储到目录服务中。您还将了解到如何配置 Acegi，使它与目录服务交互，从而实现您的访问控制策略。</p>
<br />
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-acegi1/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><span class="atitle"><a name="download">下载</a></span></p>
<table class="data-table-1" cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <th scope="col">描述</th>
            <th scope="col">名字</th>
            <th scope="col">大小</th>
            <th scope="col">下载方法</th>
        </tr>
        <tr>
            <th class="tb-row" scope="row">本文源代码</th>
            <td noWrap>j-acegi1.zip</td>
            <td noWrap>10KB</td>
            <td noWrap><a class="fbox" href="http://download.boulder.ibm.com/ibmdl/pub/software/dw/java/j-acegi1.zip" cmimpressionsent="1"><strong>HTTP</strong></a></td>
        </tr>
    </tbody>
</table>
<table cellspacing="0" cellpadding="0" border="0">
    <tbody>
        <tr valign="top">
            <td colspan="5"><img height="12" alt="" src="http://www.ibm.com/i/c.gif" width="12" border="0" /></td>
        </tr>
        <tr>
            <td><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/fw.gif" width="16" /></td>
            <td><a class="fbox" href="http://www.ibm.com/developerworks/cn/whichmethod.html" cmimpressionsent="1">关于下载方法的信息</a></td>
            <td><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="50" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="resources"><span class="atitle">参考资料 </span></a></p>
<strong>学习</strong><br />
<ul>
    <li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www.ibm.com/developerworks/java/library/j-acegi1/?S_TACT=105AGX52&amp;S_CMP=cn-a-j" target="_blank" cmimpressionsent="1">英文原文</a> 。<br />
    <br />
    <li><a href="http://www.acegisecurity.org/" cmimpressionsent="1">Acegi Security System 主页</a>： 查找参考文档的第一站，并可以下载 Acegi。<br />
    <br />
    <li>&#8220;<a href="http://www.ibm.com/developerworks/cn/java/jw-0428-security/" cmimpressionsent="1">Java 安全性的演变和理念 第一部分：安全性的基本要点</a>&#8221;（Raghavan Srinivas，developerWorks，2000 年 5 月）：对 Java 安全概念和技术进行了概述。<br />
    <br />
    <li>&#8220;<a href="http://www.ibm.com/developerworks/cn/views/java/tutorials.jsp?cv_doc_id=85017" cmimpressionsent="1">Java 安全性，第一部分： 密码学基础</a>&#8221;（Brad Rubin，developerWorks，2002 年 7 月）：全面地介绍了 Java 平台上应用程序的安全性。还可参阅 &#8220;<a href="http://www.ibm.com/developerworks/cn/views/java/tutorials.jsp?cv_doc_id=84887" cmimpressionsent="1">Java 安全性， 第二部分： 认证与授权</a>&#8221;。<br />
    <br />
    <li>&#8220;<a href="http://www.ibm.com/developerworks/web/library/wa-appsec/?S_TACT=105AGX52&amp;S_CMP=cn-a-j" cmimpressionsent="1">Web app security using Struts, servlet filters, and custom taglibs</a>&#8221;（Swaminathan Radhakrishnan，developerWorks，2004 年 9 月）：展示 Java 安全解决方案开发中的常用技巧。<br />
    <br />
    <li>&#8220;<a href="http://www.ibm.com/developerworks/cn/views/java/tutorials.jsp?cv_doc_id=156552" cmimpressionsent="1">Spring 2 和 JPA 简介</a>&#8221;（Sing Li，developerWorks，2006 年 8 月）：这篇教程将介绍第二代 Spring 框架。<br />
    <br />
    <li><a href="http://static.springframework.org/spring/docs/2.0.x/reference/index.%20html" cmimpressionsent="1">Spring 参考文档</a>：来自资源的 Spring 文档。<br />
    <br />
    <li>&#8220;<a href="http://java.sun.com/products/servlet/Filters.html" cmimpressionsent="1">The Essentials of Filters</a>&#8221;（Sun Developer Network）：了解更多过滤器及其用途。<br />
    <br />
    <li><a href="http://java.sun.com/security" cmimpressionsent="1">Java SE Security</a>：一个 Java 安全信息资料库。<br />
    <br />
    <li><a href="http://www.ibm.com/developerworks/cn/java/j-ibmsecurity.html" cmimpressionsent="1">IBM 安全提供者：概述</a>（Yanni Zhang，Audrey Timkovich 和 John Peck；developerWorks，2004 年 10 月）：了解用于 Java 平台（1.4.2 Java SDK）的 IBM 开发人员工具包增强了的安全特性，以及它与 Sun Microsystems 版本的区别。<br />
    <br />
    <li><a href="http://www.ibm.com/developerworks/cn/java/" cmimpressionsent="1">developerWorks Java 技术专区</a>：提供了大量有关 Java 编程各个方面的文章。<br />
    <br />
    </li>
</ul>
<br />
<strong>获得产品和技术</strong><br />
<ul>
    <li><a href="http://www.acegisecurity.org/downloads.html" cmimpressionsent="1">下载 Acegi Security System</a>：保护您的 Java 企业应用程序。 </li>
</ul>
<br />
<strong>讨论</strong><br />
<ul>
    <li><a href="http://www.ibm.com/developerworks/forums/dw_forum.jsp?S_TACT=105AGX52&amp;cat=5&amp;S_CMP=cn-a-j&amp;forum=375" cmimpressionsent="1">参与论坛讨论</a>。<br />
    <br />
    <li>通过参与 <a href="http://www.ibm.com/developerworks/blogs/?S_TACT=105AGX52&amp;S_CMP=cn-a-j" cmimpressionsent="1">developerWorks blog</a> 加入 <a href="http://www.ibm.com/developerworks/community?S_TACT=105AGX52&amp;S_CMP=cn-a-j" cmimpressionsent="1">developerWorks 社区</a>。 <br />
    <br />
    </li>
</ul>
<br />
<br />
<p><a name="author"><span class="atitle">关于作者</span></a></p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td colspan="3"><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /></td>
        </tr>
        <tr valign="top" align="left">
            <td>
            <p>&nbsp;</p>
            </td>
            <td><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="4" /></td>
            <td width="100%">
            <p>Bilal Siddiqui 是一名电子工程师、XML 顾问，他还是 WaxSys（主要从事电子商务简化）的创建者之一。自从 1995 年毕业于拉合尔工程技术大学（University of Engineering and Technology，Lahore）电子工程专业以后，他就开始为工业控制系统设计各种软件解决方案。稍后，他致力于 XML 方面并使用他在 C++ 编程中取得的经验来构建基于 Web 和 WAP 的 XML 处理工具、服务器端解析方案和服务应用程序。Bilal 是一名技术推广者，并且是一名多产的技术作家。</p>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
来自:http://www.cnblogs.com/amboyna/archive/2008/03/25/1122079.html
<img src ="http://www.blogjava.net/waterjava/aggbug/199309.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/waterjava/" target="_blank">狼爱上狸</a> 2008-05-08 18:41 <a href="http://www.blogjava.net/waterjava/archive/2008/05/08/199309.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>实战Acegi：使用Acegi作为基于Spring框架的WEB应用的安全框架</title><link>http://www.blogjava.net/waterjava/archive/2008/05/08/199308.html</link><dc:creator>狼爱上狸</dc:creator><author>狼爱上狸</author><pubDate>Thu, 08 May 2008 10:37:00 GMT</pubDate><guid>http://www.blogjava.net/waterjava/archive/2008/05/08/199308.html</guid><wfw:comment>http://www.blogjava.net/waterjava/comments/199308.html</wfw:comment><comments>http://www.blogjava.net/waterjava/archive/2008/05/08/199308.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/waterjava/comments/commentRss/199308.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/waterjava/services/trackbacks/199308.html</trackback:ping><description><![CDATA[<p>最近项目使用Acegi作为安全框架的实现,效果不错,就写了这篇文章作为总结.<br />
<br />
对于任何一个完整的应用系统，完善的认证和授权机制是必不可少的。在基于SpringFramework的WEB应用中，<br />
我们可以使用Acegi作为安全架构的实现。本文将介绍如何在基于Spring构架的Web应用中使用Acegi，并且详细介<br />
绍如何配置和扩展Acegi框架以适应实际需要。<br />
</p>
<p>文章和代码下载:<br />
<br />
<a href="http://www.blogjava.net/Files/youlq/Acegi.zip">http://www.blogjava.net/Files/youlq/Acegi.zip</a><br />
<br />
<br />
<strong><font color="#ff0000">注意：许多朋友在部署上遇到一些麻烦，所以我将可以部署的完整的war文件传上来，注意：java代码在acegi-sample.war\WEB-INF 目录下，例子需要Mysql，建库脚本在acegi-sample.war\db目录下。</font></strong><br />
<br />
<a href="http://www.blogjava.net/Files/youlq/acegi-sample.part1.rar"><font color="#002c99">acegi-sample.part1.rar</font></a><br />
<a href="http://www.blogjava.net/Files/youlq/acegi-sample.part2.rar"><font color="#002c99">acegi-sample.part2.rar</font></a><br />
<a href="http://www.blogjava.net/Files/youlq/acegi-sample.part3.rar"><font color="#002c99">acegi-sample.part3.rar</font></a><br />
<a href="http://www.blogjava.net/Files/youlq/acegi-sample.part4.rar"><font color="#002c99">acegi-sample.part4.rar</font></a><br />
<br />
附注：<br />
<br />
有些朋友询问我如何部署文中的例子，在此再次说明一下（文章中已经有提到）：<br />
<br />
Mysql的建表脚本在db目录下<br />
为了减小体积，已经将WEB-INF\lib下的依赖包删除，请自行下载以下包，并拷贝至WEB-INF\lib下：<br />
spring-1.2.4.jar<br />
acegi-security-0.8.3.jar<br />
aopalliance-1.0.jar<br />
c3p0-0.9.0.jar<br />
commons-logging-1.0.4.jar<br />
ehcache-1.1.jar<br />
log4j-1.2.8.jar<br />
mysql-connector-java-3.1.10-bin.jar<br />
oro-2.0.8.jar<br />
<br />
提示：<br />
acegi-security-0.8.3.jar<br />
aopalliance-1.0.jar<br />
c3p0-0.9.0.jar<br />
commons-logging-1.0.4.jar<br />
ehcache-1.1.jar<br />
log4j-1.2.8.jar<br />
oro-2.0.8.jar<br />
可以在acegi-security-0.8.3.zip所带的acegi-security-sample-contacts-filter.war中找到。<br />
spring-1.2.4.jar<br />
mysql-connector-java-3.1.10-bin.jar<br />
要分别到springframework和mysql网站下载。</p>
<br />
来自:http://www.blogjava.net/youlq/archive/2005/12/06/22678.html
<img src ="http://www.blogjava.net/waterjava/aggbug/199308.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/waterjava/" target="_blank">狼爱上狸</a> 2008-05-08 18:37 <a href="http://www.blogjava.net/waterjava/archive/2008/05/08/199308.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi的下载和安装</title><link>http://www.blogjava.net/waterjava/archive/2008/05/08/199307.html</link><dc:creator>狼爱上狸</dc:creator><author>狼爱上狸</author><pubDate>Thu, 08 May 2008 10:35:00 GMT</pubDate><guid>http://www.blogjava.net/waterjava/archive/2008/05/08/199307.html</guid><wfw:comment>http://www.blogjava.net/waterjava/comments/199307.html</wfw:comment><comments>http://www.blogjava.net/waterjava/archive/2008/05/08/199307.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/waterjava/comments/commentRss/199307.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/waterjava/services/trackbacks/199307.html</trackback:ping><description><![CDATA[<h3>1&nbsp; Acegi官方发布版的下载和安装</h3>
<p>开发者可以通过http://sourceforge.net/projects/acegisecurity或http://acegisecurity.org/下载到Acegi官方发布版，比如acegi-security-1.x.zip。图4-4展示了SF中Acegi项目的首页，它提供了下载Acegi（Spring Security）的入口。</p>
<p align="center"><img height="187" src="http://book.csdn.net/BookFiles/310/img/image011.jpg" width="300"  alt="" /></p>
<p align="center">图4-4 &nbsp;http://sourceforge.net/projects/acegisecurity首页</p>
<p>在单击图4-4中的下载（&#8220;Download Acegi Security System for Spring&#8221;）超链接后，开发者进而能够下载到最新的Acegi官方发布包。此时，建议开发者同时将acegi-security-1.x.zip、acegi-security-1.x-src.zip下载到本地，前者包含了Jar存档和若干Acegi使能应用，而后者仅仅包含了Acegi项目的源代码。</p>
<p>在下载到Acegi官方发布版后，通过解压acegi-security-1.x.zip，开发者能够浏览到如图4-5所示的类似内容。</p>
<p align="center"><img height="200" src="http://book.csdn.net/BookFiles/310/img/image012.jpg" width="300"  alt="" /></p>
<p align="center">图4-5 &nbsp;acegi-security-1.x.zip包含的内容</p>
<p>通常，大部分Acegi安全性项目仅仅需要使用到acegi-security-1.x.jar存档，这是Acegi的核心包，任何Acegi使能项目都需要使用到它。如果项目打算采用Java SE 5引入的Annotation注释支持，则还需要将acegi-security-tiger-1.x.jar添加到WEB-INF/lib中。如果开发者在使用Acegi提供的容器适配器支持，则还需要将acegi-security-catalina-1.x.jar（针对Tomcat）、acegi-security-jboss-1.x.jar（针对JBoss）、acegi-security-jetty-1.x.jar（针对Jetty）、acegi-security-resin-1.x.jar（针对Resin）等Jar存档复制到相应的位置，至于这些Jar包的具体使用，本书将在第10章详细阐述。</p>
<p>另外，acegi-security-sample-contacts-filter.war、acegi-security-sample-tutorial.war是两个直接可部署到Java EE容器（Tomcat容器）中的Web应用。</p>
<h3>2&nbsp; Subversion中的Acegi源码下载和安装</h3>
<p>如今，Acegi基代码采用Subversion管理。开发者通过如图4-6所示的Web页面能够获得Subversion下载地址（http://sourceforge.net/svn/?group_id=104215）。</p>
<p align="center"><img height="209" src="http://book.csdn.net/BookFiles/310/img/image014.jpg" width="301"  alt="" /></p>
<p align="center">图4-6 &nbsp;获得下载Acegi基代码地址的Web页面</p>
<p>事实上，Acegi（Spring Security）基代码本身就是一个Eclipse Java项目，而且它的构建、管理工作采用了Maven 1.x/2.x（http://maven.apache.org/）。开发者可以借助Eclipse Subversive插件从Subversion存储源获得Acegi的最新基代码。图4-7展示了Subversive内置的SVN Repository Exploring。</p>
<p align="center"><img height="206" src="http://book.csdn.net/BookFiles/310/img/image015.jpg" width="300"  alt="" /></p>
<p align="center">图4-7 &nbsp;Subversive插件使用截图</p>
<p>一旦在下载完成Acegi（Spring Security）基代码后，开发者将能够持续监控到Acegi项目的最新情况，比如获得Acegi持续更新的基代码、Acegi官方文档，图4-8展示了相应的使用截图。</p>
<p align="center"><img height="205" src="http://book.csdn.net/BookFiles/310/img/image016.jpg" width="290"  alt="" /></p>
<p align="center"><img height="38" src="http://book.csdn.net/BookFiles/310/img/image013.gif" width="67"  alt="" />图4-8 &nbsp;持续更新Acegi基代码</p>
<h3>3&nbsp; 有关Acegi的权威去处</h3>
<p>其一，开发者可以去订阅acegisecurity-developer@lists.sourceforge.net邮件列表，图4-9展示了订阅这一邮件列表的入口。Acegi开发团队积极参与到这一邮件列表中，因此开发者从这一邮件列表能够获得Acegi的最新进展。</p>
<p align="center"><img height="175" src="http://book.csdn.net/BookFiles/310/img/image017.jpg" width="300"  alt="" /></p>
<p align="center">图4-9 &nbsp;订阅Acegi开发者邮件列表</p>
<p>其二，Acegi官方论坛（http://forum.springframework.org/），图4-10展示了论坛截图。</p>
<p align="center"><img height="213" src="http://book.csdn.net/BookFiles/310/img/image018.jpg" width="301"  alt="" /></p>
<p align="center">图4-10 &nbsp;Acegi官方论坛</p>
<p>开发者可以通过许多渠道获得一手的Acegi知识、开发和部署经验。</p>
<h3>5&nbsp; 小结</h3>
<p>本章围绕Acegi（Spring Security）的认证策略进行了阐述，比如基于过滤器的设计、与认证源解耦、AcegiSecurityException异常体系等。另外，我们还针对Acegi发布版和基代码的下载进行了简要介绍。</p>
<p>下章将深入到Acegi支持的各种认证机制中。</p>
<h4 style="text-indent: 21pt">【参考及推荐资料】</h4>
<p>l&nbsp; http://acegisecurity.org/</p>
<p>l&nbsp; http://sourceforge.net/projects/acegisecurity</p>
<p>l&nbsp; <a href="http://www.polarion.org/index.php?page=overview&amp;project=subversive">http://www.polarion.org/index.php?page=overview&amp;project=subversive</a></p>
<br />
来自:http://book.csdn.net/bookfiles/310/10031012826.shtml<br />
<img src ="http://www.blogjava.net/waterjava/aggbug/199307.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/waterjava/" target="_blank">狼爱上狸</a> 2008-05-08 18:35 <a href="http://www.blogjava.net/waterjava/archive/2008/05/08/199307.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>