﻿<?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-Junky's IT Notebook-随笔分类-security</title><link>http://www.blogjava.net/junky/category/22553.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 16 Jan 2008 07:53:32 GMT</lastBuildDate><pubDate>Wed, 16 Jan 2008 07:53:32 GMT</pubDate><ttl>60</ttl><item><title>Windows下架设Subversion服务器(转)</title><link>http://www.blogjava.net/junky/archive/2008/01/14/175081.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Mon, 14 Jan 2008 01:38:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2008/01/14/175081.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/175081.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2008/01/14/175081.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/175081.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/175081.html</trackback:ping><description><![CDATA[<p>作者：indian</p>
<p>版本：v1.1</p>
<p>修订：2007年12月25日2:24:53</p>
<p>出处：<a href="http://indian.blog.163.com/blog/static/10881582007112415021751">blog.kysf.net</a></p>
<p style="margin-left: 31.5pt; text-indent: -31.5pt">版权：作者保留对本文的一切修改、发布等权力。任何人想要转载本文部分或全部内容时，必须保留包括作者、版本、修订、出处、版权，共五项信息。对本文的参考引用，则不受限制。</p>
<p>&nbsp;</p>
<p>关键词：subversion, 安装配置, 权限, 目录访问</p>
<p>&nbsp;</p>
<p>1 前言<br />
2 基本概念<br />
2.1 什么是版本控制<br />
2.2 什么是 Subversion<br />
2.3 版本库（repository）<br />
3 安装配置<br />
3.1 安装独立服务器 SVNServer<br />
3.2 安装 ApacheSVN 服务器<br />
4 FAQ<br />
5 参考资料</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h1 style="text-align: center">1、前 言</h1>
<p>&nbsp;</p>
<p style="text-indent: 2em">花了72小时，终于把 Subversion 初步掌握了。从一个连&#8220;什么是版本控制&#8221;都不知道的门外汉，到配置出精确至每目录访问的入门者，中间还卡了一天时间。其中费了许多气力，摸索实验了多次， 还差点放弃了，但是收获是巨大的。现把我的配置和学习过程写下来，供大家参考，也让初学者少走弯路。</p>
<p style="text-indent: 2em">由于本人不会 Unix/Linux （正在学习中），故仅以 Windows 平台为例讲解，Unix/Linux 平台请参考相关资料。如其中有谬误的地方，包括错别字，请联系我修订。</p>
<p style="color: red; text-indent: 2em"><strong>技术在分享中进步！</strong></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h1 style="text-align: center" align="center">2、基本概念</h1>
<p>&nbsp;</p>
<p>2.1、什么是版本控制</p>
<p style="text-indent: 2em">简单点来说，版本控制就是数据仓库，它可以记录你对文件的每次更改。这样，就算你在昏天黑地的改了几个月后老板说不要了，还是按照过去那样，你也不会抓狂，简单的恢复版本操作就搞定一切。</p>
<p>&nbsp;</p>
<p>2.2、什么是 Subversion</p>
<p style="text-indent: 2em">Subversion是一个自由/开源版本控制系统，它管理文件和目录可以超越时间。一组文 件存放在中心版本库，这个版本库很像一个普通的文件服务器，只是它可以记录每一次文件和目录的修改，这便使你可以取得数据以前的版本，从而可以检查所作的 更改。从这个方面看，许多人把版本控制系统当作一种&#8220;时间机器&#8221;。</p>
<p style="text-indent: 2em">Subversion可以通过网络访问它的版本库，从而使用户可以在不同的电脑上使用。一定 程度上可以说，允许用户在各自的地方修改同一份数据是促进协作。进展可能非常的迅速，并没有一个所有的改变都会取得效果的通道，由于所有的工作都有历史版 本，你不必担心由于失去某个通道而影响质量，如果存在不正确的改变，只要取消改变。</p>
<p style="text-indent: 2em">一些版本控制系统也是软件配置管理（SCM）系统，这种系统经过特定的精巧设计来管理源代 码，有许多关于软件开发的特性—本身理解编程语言、或者提供构建程序的工具。然而，Subversion不是这样一个系统，它是一个通用系统，可以管理任 何类型的文件集，对你这可能是源代码，对别人，可能是一个货物报价单或者是书稿等。</p>
<p>&nbsp;</p>
<p>2.3、版本库（repository）</p>
<p style="text-indent: 2em">Subversion 的核心就是 rpository ，中文翻译成&#8220;版本库&#8221;。就是位于服务器端，统一管理和储存数据的地方。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h1 style="text-align: center">3、安装配置</h1>
<p>&nbsp;</p>
<h3>3.1 安装独立服务器 SVNServer</h3>
<p>环境</p>
<p>OS：Windows XP SP2</p>
<p>Web：Apache 2.2.6</p>
<p>SVN：svn-win32-1.4.6</p>
<p>&nbsp;</p>
<p>一、准备工作</p>
<p>1、获取 Subversion 服务器程序</p>
<p style="text-indent: 2em">到官方网站（http://subversion.tigris.org/）下载最新的服务器安装程序。目前最新的是1.4.6版本，具体下载地址在：<a href="http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=8100&amp;expandFolder=8100&amp;folderID=91">http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=8100&amp;expandFolder=8100&amp;folderID=91</a> ，注意找 for apache 2.2.x 版本的。</p>
<p>2、获取 TortoiseSVN 客户端程序</p>
<p style="text-indent: 2em">从官方网站 <a href="http://tortoisesvn.net/downloads">http://tortoisesvn.net/downloads</a> 获取最新的 TortoiseSVN <span style="font-size: 9pt; color: black">。TortoiseSVN 是一个客户端程序，用来与 subvers 服务器端通讯。Subversion 自带一个客户端程序 svn.exe ,但 TortoiseSVN 更好操作，提高效率。</span></p>
<p>&nbsp;</p>
<p>二、安装服务器端和客户端</p>
<p style="text-indent: 2em">首先安装 Apache 2.2.6 ，具体安装方法大家参考相关资料，或者参看我写的《<a href="http://indian.blog.163.com/blog/static/1088158200755105546704">Windows下安装Apache 2.2.x</a>》。</p>
<p style="text-indent: 2em">其次安装 Subversion（以下简称SVN）的服务器端和客户端。下载下来的服务器端是个 zip压缩包，直接解压缩即可，比如我解压到 E:\subversion 。客户端安装文件是个 exe 可执行文件，直接运行按提示安装即可，客户端安装完成后提示重启。</p>
<p>&nbsp;</p>
<p>三、建立版本库（Repository）</p>
<p style="text-indent: 2em">运行Subversion服务器需要首先要建立一个版本库（Repository）。版本库可以看作是服务器上集中存放和管理数据的地方。</p>
<p style="text-indent: 2em">开始建立版本库。首先建立 e:\svn 空文件夹作为所有版本库的根目录。然后，进入命令行并切换到subversion的bin目录。输入如下命令：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">svnadmin create E:\svn\repos1</div>
<p style="text-indent: 2em">此命令在 E:\svn 下建立一个版本库 repos1 。repos1 下面会自动生成一些文件夹和文件。</p>
<p style="text-indent: 2em">我们也可以使用 TortoiseSVN 图形化的完成这一步：</p>
<p style="text-indent: 2em">先建立空目录 E:\svn\repos1 ，注意一定是要空的。然后在 repos1 文件夹上&#8220;右键-&gt;TortoiseSVN-&gt;Create Repository here...&#8221;，然后可以选择版本库模式，这里使用默认的FSFS即可，然后就创建了一系列文件夹和文件，同命令行建立的一样。</p>
<p>&nbsp;</p>
<p>四、运行独立服务器</p>
<p style="text-indent: 2em">此时 subversion 服务还没有开始，只是通过它的命令建立了版本库。继续在刚才的命令窗口输入：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">svnserve.exe &#8211;daemon</div>
<p style="text-indent: 2em">svnserve 将会在端口 3690 等待请求，--daemon（两个短横线）选项告诉 svnserve 以守护进程方式运行，这样在手动终止之前不会退出。注意不要关闭命令行窗口，关闭窗口会把 svnserve 停止。</p>
<p style="text-indent: 2em">为了验证svnserve正常工作，使用TortoiseSVN -&gt; Repo-browser 来查看版本库。在弹出的 URL 对话框中输入：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">svn://localhost/svn/repo1</div>
<p style="text-indent: 2em">点 OK 按钮后就可以看见 repo1 版本库的目录树结构了，只不过这时 repo1 是个空库。</p>
<p style="text-indent: 2em">你也可以使用--root选项设置根位置来限制服务器的访问目录，从而增加安全性和节约输入svnserve URL的时间：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">svnserve.exe --daemon --root drive:\path\to\repository</div>
<p style="text-indent: 2em">以前面的测试作为例，svnserve 将会运行为：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">svnserve.exe --daemon --root e:\svn</div>
<p style="text-indent: 2em">然后TortoiseSVN中的版本库浏览器URL缩减为：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">svn://localhost/repo1</div>
<p>&nbsp;</p>
<p>五、配置用户和权限</p>
<p style="text-indent: 2em">用文本编辑器打开E:\svn\repos1\conf目录，修改svnserve.conf：</p>
<p>将：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial"># password-db = passwd</div>
<p>改为：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">password-db = passwd</div>
<p style="text-indent: 2em">即去掉前面的 # 注释符，注意前面不能有空格。</p>
<p style="text-indent: 2em">然后修改同目录的passwd文件，增加一个帐号：</p>
<p>将：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">[users]<br />
# harry = harryssecret<br />
# sally = sallyssecret</div>
<p>增加帐号：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">[users]<br />
#harry = harryssecret<br />
#sally = sallyssecret<br />
test = test</div>
<p>&nbsp;</p>
<p>六、初始化导入</p>
<p style="text-indent: 2em">下面就是将我们的数据（项目）导入到这个版本库，以后就由版本库管理我们的数据。我们的任何改动都回被版本库记录下来，甚至我们自己丢失、改错数据时版本库也能帮我们找回数据。</p>
<p style="text-indent: 2em">比如，我在 d:\wwwroot 下有个 guestbook 文件夹，里面存放的是我编写的留言簿程序。在此文件夹上&#8220;右键 -&gt; TortoiseSVN -&gt; Import...&#8221; ，在弹出对话框的&#8220;URL of repository&#8221;输入&#8220;svn://localhost/repos1/guestbook&#8221;。在&#8220;Import message&#8221;输入&#8220;导入整个留言簿&#8221;作为注释。<br />
点 OK 后要求输入帐号。我们在用户名和密码处都输入 test 。完成后 guestbook 中的内容全部导入到了 svn://localhost/svn/repo1/guestbook 。</p>
<p style="text-indent: 2em">我们看到在 e:\svn\repo1 没有任何变化，连个 guestbook 文件夹都没有建立，唯一的变化就是e:\svn\repo1容量变大了。实际上我们源guestbook中的内容已经导入 repo1 版本库了，源 guestbook 文件夹可以删除了。</p>
<p style="text-indent: 2em">需要注意的是，这一步操作可以完全在另一台安装了 TortoiseSVN 的客户机上进行。例如运行svnserve的主机的IP是133.96.121.22，则URL部分输入的内容就是&#8220;svn://133.96.121.22&#8221; 。</p>
<p>&nbsp;</p>
<p>七、基本操作流程</p>
<p>1、取出（check out）</p>
<p>取出版本库到一个工作拷贝：</p>
<p style="text-indent: 2em">来到任意空目录下，比如在f分区建立一个空文件夹 f:\work 。&#8220;右键 -&gt; SVN Checkout&#8221;。在&#8220;URL of repository&#8221;中输入&#8220;svn://localhost/svn/repo1/guestbook&#8221;，这样我们就得到了一份 guestbook 中内容的工作拷贝。</p>
<p>2、存入（check in）/提交（commit）</p>
<p>在工作拷贝中作出修改并提交：</p>
<p style="text-indent: 2em">在 guestbook 工作拷贝中随便打开一个文件，作出修改，然后&#8220;右键 -&gt; SVN Commit... &#8221;。这样我们就把修改提交到了版本库，版本库根据情况存储我们提交的数据。</p>
<p style="text-indent: 2em">在修改过的文件上&#8220;右键 -&gt; TortoiseSVN -&gt; Show Log&#8221; ，可以看到对这个文件所有的提交。在不同的 revision 条目上&#8220;右键 -&gt; Compare with working copy&#8221;，我们可以比较工作拷贝的文件和所选 revision 版本的区别。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h3>3.2 安装 ApacheSVN 服务器</h3>
<p style="text-indent: 2em">Subversion的设计包括一个抽象的网络层，这意味着版本库可以通过各种服务器进程访问。理论上讲，Subversion可以使用无限数量的网络协议实现，目前实践中存在着两种服务器。</p>
<p>&nbsp;</p>
<ul>
    <li>SVNServer：svnserve 是一个小的（也叫轻型的）、独立服务器，使用自己定义的协议和客户端。（作者注：以下称这种服务器为&#8220;svnserver服务器&#8221;，上面的安装配置就是安装svnserver服务器。） <br />
    <br />
    <li>ApacheSVN：Apache是最流行的web服务器，通过使用 mod_dav_svn 模块，Apache可以访问版本库，并且可以使客户端使用HTTP的扩展协议WebDAV/DeltaV进行访问。（作者注：以下称这种服务器为&#8220;ApacheSVN服务器&#8221;） </li>
</ul>
<p>&nbsp;</p>
<p style="text-indent: 2em">通过 Http 协议访问版本库是 Subversion 的亮点之一。<strong>ApacheSVN服务器</strong> 具备了许多 <strong>svnserve服务器 </strong>没有的特性，使用上更加灵活，但是有一点难于配置，灵活通常会带来复杂性。</p>
<p style="text-indent: 2em">由于 Subversion 需要版本化的控制，因此标准的 Http 协议不能满足需求。要让 Apache 与 Subversion 协同工作，需要使用 WebDAV（Web-based Distributed Authoring and Versioning：）Web 分布式创作和版本控制）。WebDAV 是 HTTP 1.1 的扩展，关于 WebDAV 的规范和工作原理，可以参考 <a href="http://www.ietf.org/rfc/rfc2518.txt">IETF RFC 2518</a> （<a href="http://www.ietf.org/rfc/rfc2518.txt">http://www.ietf.org/rfc/rfc2518.txt</a>）。</p>
<p>&nbsp;</p>
<p>一、必备条件</p>
<p style="text-indent: 2em">为了让你的版本库使用HTTP网络，你必需具备以下几个条件：</p>
<ol>
    <li>配置好httpd 2.2.x，并且使用mod_dav启动。
    <li>为mod_dav安装mod_dav_svn插件。
    <li>配置你的httpd.conf，使http协议能访问版本库。 </li>
</ol>
<p style="text-indent: 2em">下面以我的配置过程详细讲解。</p>
<p>环境：</p>
<p style="text-indent: 2em">OS：Windows XP SP2</p>
<p style="text-indent: 2em">Web：Apache 2.2.6</p>
<p style="text-indent: 2em">SVN：svn-win32-1.4.6</p>
<p>&nbsp;</p>
<p>二、安装</p>
<p>1、安装Apache</p>
<p style="text-indent: 2em">具体安装方法见：《<a href="http://indian.blog.163.com/blog/static/1088158200755105546704">Windows下安装Apache 2.2.x</a>》</p>
<p>2、安装 Subversion</p>
<p style="text-indent: 2em">将下载下来的 svn-win32-1.4.6.zip 直接解压即可，比如我解压到 e:\subversion 。<br />
从Subversion安装目录的 bin 子目录将 intl3_svn.dll、libdb44.dll、mod_authz_svn.so、mod_dav_svn.so 拷贝到Apache的模块目录（Apache 安装目录的 modules 文件夹）。</p>
<p>&nbsp;</p>
<p>三、基本的Apache配置</p>
<p style="text-indent: 2em">修改Apache的配置文件 <em>httpd.conf </em>，使用LoadModule来加载mod_dav_svn模块。</p>
<p>将：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">#LoadModule dav_module modules/mod_dav.so</div>
<p>改成：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">LoadModule dav_module modules/mod_dav.so</div>
<p style="text-indent: 2em">即去掉前面的&#8220;#&#8221;号。</p>
<p>添加：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">LoadModule dav_svn_module modules/mod_dav_svn.so</div>
<p style="text-indent: 2em">一定确定它在 mod_dav 之后。</p>
<p style="text-indent: 2em">现在你已经设置了Apache和Subversion，但是Apache不知道如何处理 Subversion客户端，例如TortoiseSVN。为了让Apache知道哪个目录是用来作为Subversion版本库，你需要使用编辑器（例 如记事本）编辑Apache的配置文件。</p>
<p style="text-indent: 2em">在配置文件最后添加如下几行：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">&lt;Location / repository&gt;<br />
DAV svn<br />
SVNPath e:/svn/repos1<br />
&lt;/Location&gt;</div>
<p style="text-indent: 2em">这个配置告诉Apache首先需要启用 dav_module，然后加载 dav_svn_module 。版本库对外的URL是：http://服务器IP/repository ，所有的Subversion版本库在物理上位于e:/svn/repos1 。</p>
<p style="text-indent: 2em">配置完毕后重新启动 Apache，打开浏览器，输入 http://服务器IP/ repository 将会看到如下画面：</p>
<p style="margin: 25pt; text-align: center"><img height="104" src="http://img.blog.163.com/photo/94bq16Cb2CSM3YMOo_WcWQ==/880172252174552815.jpg" width="324" border="0"  alt="" /></p>
<p style="text-indent: 2em">这表示 Apache 的 dav_svn 模块已经可以正常工作了。用户可以使用任何一种 Subversion 的客户端通过 Http 协议访问你的版本库。</p>
<p style="text-indent: 2em">如果想要指定多个版本库，可以用多个 Location 标签，也可以使用 SVNParentPath 代替 SVNPath，例如在 e:\svn 下有多个版本库 repos1，repos2 等等，用如下方式指定：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">&lt;Location /repository&gt;<br />
DAV svn<br />
SVNParentPath e:/svn<br />
&lt;/Location&gt;</div>
<p style="text-indent: 2em">&#8220;SVNParentPath e:/svn &#8221; 表示 e:\svn 下的每个子目录都是一个版本库。可以通过 http://服务器IP/repository/repos1，http://服务器IP/repository/repos2 来访问。</p>
<p style="text-indent: 2em">现在你的版本库任何人都可以访问，并且有完全的写操作权限。也就是说任何人都可以匿名读取， 修改，提交，以及删除版本库中的内容(注：这时不需要配置E:\svn\repos\conf\svnserve.conf 文件，并且也不需要启动E:\subversion\bin\svnserve.exe。因为提交是通过Apache的dav模块处理的，而不是由 svnservice处理。)。我们用 TortoiseSVN 客户端验证即知。</p>
<p style="text-indent: 2em">显然大部分场合这是不符合需求的。那么如何进行权限设置呢，Apache 提供了基本的权限设置：</p>
<p>&nbsp;</p>
<p>四、认证选项</p>
<p>1、基本 HTTP 认证</p>
<p style="text-indent: 2em">最简单的客户端认证方式是通过 HTTP 基本认证机制，简单的使用用户名和密码来验证一个用户的身份。Apache提供了一个 htpasswd 工具来管理一个用户文件，这个文件包含用户名和加密后的密码，这些就是你希望赋予 Subversion 特别权限的用户。htpasswd 可以在 Apache 的 bin 安装目录下找到。具体使用方法如下：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">
<p>创建用户文件：<br />
htpasswd -c E:\usr\Apache2.2\bin\passwd.conf username</p>
<p>添加新用户（-m 表示以 MD5 加密密码，可选项）：<br />
htpasswd [-m] E:\usr\Apache2.2\bin\passwd.conf Newusername</p>
<p>更改用户密码：<br />
htpasswd [-m] E:\usr\Apache2.2\bin\passwd.conf username</p>
<p>删除用户（要用大写的 D ）：<br />
htpasswd &#8211;D E:\usr\Apache2.2\bin\passwd.conf username</p>
</div>
<p style="text-indent: 2em">接下来修改 httpd.conf，在 Location 标签中加入如下内容：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">AuthType Basic<br />
AuthName "svn repos"<br />
AuthUserFile E:/usr/Apache2.2/bin/passwd.conf<br />
Require valid-user</div>
<p style="text-indent: 2em">说明：</p>
<p style="text-indent: 2em">AuthType Basic：启用基本的验证，比如用户名/密码对。</p>
<p style="text-indent: 2em">AuthName "svn repos"：当一个认证对话框弹出时，出现在认证对话框中的信息。（最好用英文，TortoiseSVN 不支持中文，安装语言包除外。）</p>
<p style="text-indent: 2em">AuthUserFile E:/usr/Apache2.2/bin/passwd：指定E:\usr\Apache2.2\bin\passwd为用户文件，用来验证用户的用户名及密码。</p>
<p style="text-indent: 2em">Require valid-user：限定用户只有输入正确的用户名及密码后才能访问这个路径</p>
<p>&nbsp;</p>
<p style="text-indent: 2em">重新启动 Apache ，打开浏览器访问版本库。Apache 会提示你输入用户名和密码来认证登陆了，现在只有 passwd 文件中设定的用户才可以访问版本库。也可以配置只有特定用户可以访问，替换上述 "Require valid-user" 为 "Require user tony robert" 将只有用户文件中的 tony 和 robert 可以访问该版本库。</p>
<p style="text-indent: 2em">有的时候也许不需要这样严格的访问控制，例如大多数开源项目允许匿名的读取操作，而只有认证用户才允许写操作。为了实现更为细致的权限认证，可以使用 Limit 和 LimitExcept 标签。例如：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">&lt;LimitExcept GET PROPFIND OPTIONS REPORT&gt;<br />
require valid-user<br />
&lt;/LimitExcept&gt;</div>
<p style="text-indent: 2em">以上配置将使匿名用户有读取权限，而限制只有 passwd 中配置的用户可以使用写操作。</p>
<p style="text-indent: 2em">如果这还不能满足你的要求，你希望精确的控制版本库目录访问，可以使用 Apache 的 mod_authz_svn 模块对每个目录进行认证操作。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>2、用 mod_authz_svn 进行目录访问控制</p>
<p style="text-indent: 2em">首先需要让 Apache 将 mod_authz_svn 模块加载进来。在 Subversion 的安装目录中找到 mod_auth_svn 模块，将其拷贝到 Apache 安装目录的 modules 子目录下。修改 httpd.conf 文件，添加：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">LoadModule authz_svn_module modules/mod_authz_svn.so</div>
<p style="text-indent: 2em">现在可以在 Location 标签中使用 authz 的功能了。一个基本的 authz 配置如下：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">&lt;Location /repository&gt;<br />
DAV svn<br />
SVNParentPath e:/svn<br />
<br />
# our access control policy<br />
AuthzSVNAccessFile E:/usr/Apache2.2/bin/accesspolicy.conf<br />
<br />
# try anonymous access first, resort to real<br />
# authentication if necessary.<br />
Satisfy Any<br />
Require valid-user<br />
<br />
# how to authenticate a user<br />
AuthType Basic<br />
AuthName "Subversion repository"<br />
AuthUserFile E:/usr/Apache2.2/bin/passwd.conf<br />
&lt;/Location&gt;</div>
<p style="text-indent: 2em">AuthzSVNAccessFile 指向的是 authz 的策略文件，详细的权限控制可以在这个策略文件中指定。访问文件 accesspolicy.conf 的语法与svnserve.conf和 Apache 的配置文件非常相似，以（#）开头的行会被忽略；在它的简单形式里，每一小节命名一个版本库和一个里面的路径；认证用户名是在每个小节中的选项名；每个选 项的值描述了用户访问版本库的级别：r（只读）或者rw（读写），如果用户没有提到或者值留空，访问是不允许的； * 表示所有用户，用它控制匿名用户的访问权限；@符号区分组和用户。如：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">[groups]<br />
committers = paulex, richard<br />
developers = jimmy, michel, spark, sean<br />
<br />
[/]<br />
* = r<br />
@committers = rw <br />
<br />
[/branches/dev]<br />
@developers = rw <br />
<br />
[/tags]<br />
tony = rw <br />
[/private]<br />
* = <br />
@committers= r</div>
<p style="text-indent: 2em">使用 SVNParentPath 代替 SVNPath 来指定多个版本库的父目录时，其中所有的版本库都将按照这个策略文件配置。例如上例中 tony 将对所有版本库里的 /tags 目录具有读写权限。如果要对具体每个版本库配置，用如下的语法：</p>
<div style="border-right: 1pt solid; padding-right: 4pt; border-top: 1pt solid; padding-left: 4pt; background: silver 0% 50%; padding-bottom: 4pt; margin: 30pt 21pt; border-left: 1pt solid; padding-top: 4pt; border-bottom: 1pt solid; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">[groups]<br />
project1_committers = paulex, richard<br />
project2_committers = jimmy, michel, spark, tony, Robert<br />
<br />
[repos1:/] <br />
* = r <br />
@ project1_committer = rw<br />
<br />
[repos2:/] <br />
* = r <br />
@ project2_committer = rw</div>
<p style="text-indent: 2em">这样项目1的 project1_committer 组只能对 repos1 版本库下的文件具有写权限而不能修改版本库 repos2 ，同样项目2的 project2_commiter 组也不能修改 repos1 版本库的文件。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h1 style="text-align: center">4、FAQ</h1>
<p>1、路径或权限不足时将出现错误信息提示：</p>
<p>http://localhost （路径不对）<br />
Error * PROPFIND request failed on '/' PROPFIND of '/': 200 OK (http://localhost)</p>
<p>http://localhost/svn （权限不足） <br />
Error * PROPFIND request failed on '/svn' PROPFIND of '/svn': 403 Forbidden (http://localhost)</p>
<p>http://localhost/svn/repos （正常显示）</p>
<p>http://localhost/repos （权限不允许） <br />
Error * PROPFIND request failed on '/repos' PROPFIND of '/repos': 405 Method Not Allowed (http://localhost)</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>2、不启动E:\subversion\bin\svnserve.exe ，但启动了ApacheSVN ，访问（tortoiseSVN &#8211;&gt; Repo &#8211; browser）或提交（SVN Commit）情形如下：</p>
<p style="text-indent: 2em">现象：svn://localhost/svn/repos 不能访问或提交，提示：Error * Can't connect to host 'localhost': 由于目标机器积极拒绝，无法连接。 但 file:///e:/svn/repos 和 http://localhost/svn/repos 可以访问或提交。</p>
<p style="text-indent: 2em">原因：svn:// 是独立服务器 svnserver 自己的协议。file:/// 是本地访问，即服务器端和客户端在一个机器上。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>参考资料：<br />
<a href="http://subversion.tigris.org/">Subversion 官方网站</a>：Subversion 的官方网站，提供最权威的介绍和最新的下载。<br />
<a href="http://www.subversion.org.cn/svnbook/">Subversion 中文手册</a>：Subversion 简体中文官方网站翻译的中文手册。<br />
<a href="http://www.subversion.org.cn/tsvndoc/">TortoiseSVN 中文文档</a>：Subversion 简体中文官方网站翻译的 TortoiseSVN 中文文档。<br />
<a href="http://www.iusesvn.com/">我用SVN中文论坛</a>：国内人气非常旺的 SVN 中文交流论坛。<br />
《<a href="http://www.subversion.org.cn/index.php?option=com_content&amp;task=view&amp;id=78&amp;Itemid=9">用Apache和Subversion搭建安全的版本控制环境</a>》：IBM 工程师写的基于 Linux 的教程。<br />
<a href="http://baike.baidu.com/">百度百科</a>：由全体网民共同撰写的百科全书</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>~ 全文完~</p>
<img src ="http://www.blogjava.net/junky/aggbug/175081.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2008-01-14 09:38 <a href="http://www.blogjava.net/junky/archive/2008/01/14/175081.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CAS中使用自己的Credentials（转）</title><link>http://www.blogjava.net/junky/archive/2007/08/20/138136.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Mon, 20 Aug 2007 05:58:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/08/20/138136.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/138136.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/08/20/138136.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/138136.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/138136.html</trackback:ping><description><![CDATA[<p>Yale CAS 3.1<br>下载: <a href="http://www.ja-sig.org/products/cas/index.html"><u><font color=#800080>http://www.ja-sig.org/products/cas/index.html</font></u></a> </p>
<p><strong><font size=3>1. 修改authenticationViaFormAction以使用自己的Credentials</font></strong></p>
<p>默认的org.jasig.cas.authentication.principal.UsernamePasswordCredentials只记录用户名和密码，在扩展一些属性如验证码时使用用自己的Credentials类替换</p>
<p><strong>cas-servlet.xml:</strong><br>&lt;bean id="authenticationViaFormAction" class="org.jasig.cas.web.flow.AuthenticationViaFormAction"<br>&nbsp;&nbsp; p:centralAuthenticationService-ref="centralAuthenticationService"<br>&nbsp;&nbsp; p:warnCookieGenerator-ref="warnCookieGenerator"<br>&nbsp;&nbsp; p:formObjectName="credentials"<br>&nbsp;&nbsp; p:formObjectClass="com.nlcd.cas.authentication.principal.EcardCredentials"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="validator"&gt;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean class="com.nlcd.cas.validation.EcardCredentialsValidator"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&lt;/bean&gt;</p>
<p><strong>EcardCredentialsValidator:</strong><br>import org.springframework.validation.Errors;<br>import org.springframework.validation.ValidationUtils;<br>import org.springframework.validation.Validator;<br>import com.nlcd.cas.authentication.principal.EcardCredentials;</p>
<p>public final class EcardCredentialsValidator implements Validator {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; public boolean supports(final Class clazz) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return EcardCredentials.class.isAssignableFrom(clazz);<br>&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; public void validate(final Object o, final Errors errors) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "required.username", null);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "required.password", null);<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>}</p>
<p><strong>EcardCredentials: (加入一个idtype属性)</strong><br>import org.jasig.cas.authentication.principal.Credentials;</p>
<p>public class EcardCredentials implements Credentials {</p>
<p>/** Unique ID for serialization. */<br>private static final long serialVersionUID = -7863273946921255486L;<br><br>private String idtype;</p>
<p>/** The username. */<br>&nbsp;&nbsp;&nbsp;&nbsp; private String username;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; /** The password. */<br>&nbsp;&nbsp;&nbsp;&nbsp; private String password;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; public String getIdtype() {<br>&nbsp;&nbsp; return idtype;<br>}</p>
<p>public void setIdtype(String idtype) {<br>&nbsp;&nbsp; this.idtype = idtype;<br>}</p>
<p>/**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @return Returns the password.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp; public final String getPassword() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.password;<br>&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; /**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @param password The password to set.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp; public final void setPassword(final String password) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.password = password;<br>&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; /**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @return Returns the userName.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp; public final String getUsername() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.username;<br>&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; /**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * @param userName The userName to set.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp; public final void setUsername(final String userName) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.username = userName;<br>&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; public String toString() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.username;<br>&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; public boolean equals(final Object obj) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (obj == null || !obj.getClass().equals(this.getClass())) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final EcardCredentials c = (EcardCredentials) obj;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.idtype.equals(c.getIdtype()) &amp;&amp; this.username.equals(c.getUsername())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; this.password.equals(c.getPassword());<br>&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; public int hashCode() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return this.idtype.hashCode() ^ this.username.hashCode() ^ this.password.hashCode();<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>}</p>
<p><strong><font size=3>2. 部署自己的authenticationHandlers</font></strong></p>
<p><strong>deployerConfigContext.xml:</strong><br>&lt;property name="credentialsToPrincipalResolvers"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="com.nlcd.cas.authentication.principal.EcardCredentialsToPrincipalResolver" /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" /&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/list&gt;<br>&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp; &lt;property name="authenticationHandlers"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p:httpClient-ref="httpClient" /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="com.nlcd.cas.authentication.handler.support.EcardAuthenticationHandler" /&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/list&gt;<br>&nbsp;&nbsp; &lt;/property&gt;</p>
<p><strong>EcardCredentialsToPrincipalResolver:</strong><br>import org.apache.commons.logging.Log;<br>import org.apache.commons.logging.LogFactory;<br>import org.jasig.cas.authentication.principal.CredentialsToPrincipalResolver;<br>import org.jasig.cas.authentication.principal.Credentials;<br>import org.jasig.cas.authentication.principal.Principal;<br>import org.jasig.cas.authentication.principal.SimplePrincipal;</p>
<p>public final class EcardCredentialsToPrincipalResolver implements<br>&nbsp;&nbsp;&nbsp;&nbsp; CredentialsToPrincipalResolver {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; /** Logging instance. */<br>&nbsp;&nbsp;&nbsp;&nbsp; private final Log log = LogFactory.getLog(getClass());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; public Principal resolvePrincipal(final Credentials credentials) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; final EcardCredentials ecardCredentials = (EcardCredentials) credentials;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (log.isDebugEnabled()) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log.debug("Creating SimplePrincipal for ["<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; + ecardCredentials.getUsername() + "]");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new SimplePrincipal(ecardCredentials.getUsername());<br>&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; public boolean supports(final Credentials credentials) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return credentials != null<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; EcardCredentials.class.isAssignableFrom(credentials<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .getClass());<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>}</p>
<p><strong>EcardAuthenticationHandler:</strong><br>import org.jasig.cas.authentication.handler.AuthenticationException;<br>import org.jasig.cas.authentication.handler.AuthenticationHandler;<br>import org.jasig.cas.authentication.principal.Credentials;<br>import org.jasig.cas.util.annotation.NotNull;<br>import com.nlcd.cas.authentication.principal.EcardCredentials;</p>
<p>public final class EcardAuthenticationHandler implements AuthenticationHandler {</p>
<p>private static final Class&lt;EcardCredentials&gt; DEFAULT_CLASS = EcardCredentials.class;</p>
<p>/** Class that this instance will support. */<br>@NotNull<br>private Class&lt;?&gt; classToSupport = DEFAULT_CLASS;</p>
<p>private boolean supportSubClasses = true;</p>
<p>public EcardAuthenticationHandler() {<br>}</p>
<p>public final boolean authenticate(final Credentials credentials)<br>&nbsp;&nbsp;&nbsp; throws AuthenticationException {<br>&nbsp;&nbsp; //TODO: your code here<br>&nbsp;&nbsp; return true;<br>}</p>
<p>public final boolean supports(final Credentials credentials) {<br>&nbsp;&nbsp; return credentials != null<br>&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; (this.classToSupport.equals(credentials.getClass()) || (this.classToSupport<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .isAssignableFrom(credentials.getClass()))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;&amp; this.supportSubClasses);<br>}<br>}</p>
<p><strong><font size=3>3. 配置Tomcat使用SSL安全认证</font></strong></p>
<p><strong>生成服务器端密钥:</strong><br>keytool -genkey -alias nlcdcas -keyalg RSA -keypass&nbsp;changeit -storepass&nbsp;changeit -keystore server.keystore<br>您的名字与姓氏是什么？<br>&nbsp; [192.168.61.56]：&nbsp; 192.168.61.56<br>您的组织单位名称是什么？<br>&nbsp; [nlce]：&nbsp; nlcd<br>您的组织名称是什么？<br>&nbsp; [Unknown]：&nbsp; nlcd<br>您所在的城市或区域名称是什么？<br>&nbsp; [Unknown]：&nbsp; beijing<br>您所在的州或省份名称是什么？<br>&nbsp; [Unknown]：&nbsp; beijing<br>该单位的两字母国家代码是什么<br>&nbsp; [Unknown]：&nbsp; cn<br>CN=192.168.61.56, OU=nlcd, O=nlcd, L=beijing, ST=beijing, C=cn 正确吗？<br>&nbsp; [否]：&nbsp; y</p>
<p><strong>生成服务器端证书:</strong><br>keytool -export -alias nlcdcas -storepass changeit&nbsp;-file server.cer -keystore server.keystore<br><br><strong><span>导入证书文件到</span><span>cacerts</span><span> 文件中:</span></strong><br><span>keytool -import -trustcacerts -alias server -file server.cer -keystore cacerts -storepass changeit</span></p>
<p><span><span>把</span><span>cacerts文件，拷贝到</span><span>&lt;JAVA_HOME&gt;\jre\lib\security目录下;server.keystore拷贝到Tomcat安装目录下</span></span></p>
<p><span><span><span>修改</span><span>Tomcat</span><span>的配置文件</span><span>server.xml</span><span>把以下补注释的内容打开</span></span></span></p>
<p><span>&lt;Connector port="8443" maxHttpHeaderSize="8192"</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>maxThreads="150" minSpareThreads="25" maxSpareThreads="75"</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>enableLookups="false" disableUploadTimeout="true"</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>acceptCount="100" scheme="https" secure="true"</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>clientAuth="false" sslProtocol="TLS" /&gt;</span></p>
<p><span>加入红字部份后的内容如下：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>&lt;Connector port="8443" maxHttpHeaderSize="8192"</span><span> </span></p>
<p><font color=#ff0000><span>keystorePass="changeit" keystoreFile="/</span><span>server.keystore</span><span>"</span></font></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>maxThreads="150" minSpareThreads="25" maxSpareThreads="75"</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>enableLookups="false" disableUploadTimeout="true"</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>acceptCount="100" scheme="https" secure="true"</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>clientAuth="false" sslProtocol="TLS" /&gt;</span></p>
<img src ="http://www.blogjava.net/junky/aggbug/138136.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-08-20 13:58 <a href="http://www.blogjava.net/junky/archive/2007/08/20/138136.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>14.1  Acegi眼中的领域对象</title><link>http://www.blogjava.net/junky/archive/2007/07/03/127937.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Tue, 03 Jul 2007 11:24:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/07/03/127937.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/127937.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/07/03/127937.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/127937.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/127937.html</trackback:ping><description><![CDATA[<h3>14.1 &nbsp;Acegi眼中的领域对象</h3>
<p>大部分开发者都应该熟悉基于Windows NT内核的Windows操作系统，比如Windows 2000、2003、XP。事实上，Windows操作系统本身充当了管理千万个领域对象的角色，这里的领域对象就是那些文件夹和文件。各个文件夹可以含有子文件夹，也可以含有大量的文件。文件是叶节点，它不再包含任何子元素。从领域对象的角度出发，文件夹和文件自身都持有各自的访问控制列表（Access Control List，ACL），ACL用于给定操控这些领域对象的权限信息，比如marissa用户可以操作shared目录，而scott用户不能够操作shared目录等。</p>
<p>为了同基于角色授权区分开，对于领域对象而言，访问控制列表（Access Control List，ACL）成为了各个领域对象的&#8220;专有名词&#8221;。这意味着，角色授权适用于Web资源和业务方法，而ACL授权适用于领域对象。各个ACL可能持有若干个ACE，即Access Control Entry（访问控制项）。总之，各个领域对象都会存在对应它的ACL，而各个ACL会持有若干个ACE，ACE真正给出了操控当前领域对象的具体权限信息。</p>
<table cellSpacing=0 cellPadding=0 width=519 border=1>
    <thead>
        <tr>
            <td width=519>
            <p>关于org.acegisecurity.acl与org.acegisecurity.acls包</p>
            </td>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td width=519>
            <p>早期的Acegi就提供了较好的领域对象支持，即org.acegisecurity.acl包。随着企业用户的日益使用，他们逐渐认识到org.acegisecurity.acl的不足之处，比如他们不能够将特定数据库提供的一些高级特性应用到自身的应用中、acl包操作数据库的效率较低、对内存的使用不是非常合理等。后来，Acegi开发团队便也在逐渐改进现有的不足之处。</p>
            <p>自从Acegi 1.0.3开始，基于新基代码的org.acegisecurity.acls包取代了org.acegisecurity.acl的地位，也就是说新开发的企业应用最好使用org.acegisecurity.acls提供的领域对象支持。本书仅仅专注org.acegisecurity.acls的使用，这两个包的使用存在的差别很大。可以看出，acls包不仅克服了原有acl包的一切缺陷，而且Acegi开发团队一直在改进acls包。Acegi开发团队可能会在某个特定时刻将org.acegisecurity.acl包丢弃掉。</p>
            </td>
        </tr>
    </tbody>
</table>
<h3><img height=38 src="http://book.csdn.net/BookFiles/310/img/image005.gif" width=67>14.1.1 &nbsp;保护领域对象概述</h3>
<p>Acegi于org.acegisecurity.acls.domain包内置了表示ACL的如下Acl接口。通过这一接口，我们能够获得ACL持有的ACE集合、当前ACL对应的领域对象、这一ACL的持有人（主人）、当前ACL的父ACL等。</p>
<p>public interface Acl extends <strong>Serializable</strong> {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //获得当前ACL持有的ACE集合</p>
<p>&nbsp;&nbsp;&nbsp; public AccessControlEntry[] <strong>getEntries</strong>();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //当前ACL对应的领域对象</p>
<p>&nbsp;&nbsp;&nbsp; public ObjectIdentity <strong>getObjectIdentity</strong>();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //持有这一ACL的主人</p>
<p>&nbsp;&nbsp;&nbsp; public Sid <strong>getOwner</strong>();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //获得当前ACL的父ACL</p>
<p>&nbsp;&nbsp;&nbsp; public Acl <strong>getParentAcl</strong>();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //父ACL是否允许被当前ACL继承</p>
<p>&nbsp;&nbsp;&nbsp; public boolean <strong>isEntriesInheriting</strong>();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //用于ACL授权决定</p>
<p>&nbsp;&nbsp;&nbsp; public boolean <strong>isGranted</strong>(Permission[] permission, Sid[] sids, </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; boolean administrativeMode)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws NotFoundException, UnloadedSidException;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //判断当前ACL是否已持有传入的sids</p>
<p>&nbsp;&nbsp;&nbsp; public boolean <strong>isSidLoaded</strong>(Sid[] sids);</p>
<p>}</p>
<p>图14-1展示了Acegi内置的Acl继承链，处于继承链中的各个接口及类各有各的用途。比如，借助于MutableAcl能够修改当前的AclImpl实例，借助于AuditableAcl能够完成ACL中ACE的审计，借助于OwnershipAcl能够修改当前AclImpl实例的主人。</p>
<p align=center><img height=185 src="http://book.csdn.net/BookFiles/310/img/image021.gif" width=326 border=0></p>
<p align=center>图14-1 &nbsp;Acl继承链</p>
<p>下面给出了Acegi内置的用于表示ACE的AccessControlEntry策略接口。通常，Acl同AccessControlEntry具有1:<em>N</em>的关系，单个Acl持有若干个AccessControlEntry。</p>
<p>public interface AccessControlEntry {</p>
<p>&nbsp;&nbsp;&nbsp; //获得其所在的ACL</p>
<p>&nbsp;&nbsp;&nbsp; public Acl <strong>getAcl</strong>();</p>
<p>&nbsp;&nbsp;&nbsp; //标识当前的ACE</p>
<p>&nbsp;&nbsp;&nbsp; public Serializable <strong>getId</strong>();</p>
<p>&nbsp;&nbsp;&nbsp; //表示的ACL（ACE）权限信息</p>
<p>&nbsp;&nbsp;&nbsp; public Permission <strong>getPermission</strong>();</p>
<p>&nbsp;&nbsp;&nbsp; //当前ACL（ACE）权限信息的持有人，比如marissa用户</p>
<p>&nbsp;&nbsp;&nbsp; public Sid <strong>getSid</strong>();</p>
<p>&nbsp;&nbsp;&nbsp; //当前ACL（ACE）权限信息是否已经授给了Sid</p>
<p>&nbsp;&nbsp;&nbsp; public boolean <strong>isGranting</strong>();</p>
<p>}</p>
<p>图14-2展示了Acegi内置的AccessControlEntry继承链。</p>
<p align=center><img height=143 src="http://book.csdn.net/BookFiles/310/img/image022.gif" width=277 border=0></p>
<p align=center>图14-2 &nbsp;AccessControlEntry继承链</p>
<p>在借助Acegi保护领域对象期间，开发者几乎不用同Acl和AccessControlEntry打交道，至少不用同AclImpl和AccessControlEntryImpl实现类交互。Acl和AccessControlEntry都使用到Sid接口，而Acl还使用到ObjectIdentity接口。Sid用于表示ACL授权过程中的授权对象，比如marissa用户、ROLE_USER角色都可以成为Sid的表示对象。图14-3展示了Acegi内置的Sid继承链。开发者将用户名、Authentication对象传入PrincipalSid构建器便能够构建出PrincipalSid对象；同理，将角色、GrantedAuthority对象传入GrantedAuthoritySid构建器便能够构建出GrantedAuthoritySid对象。ObjectIdentity用于标识单个领域对象，这一对象的存在使得目标企业应用同Acegi间的耦合得到降低，ObjectIdentityImpl是Acegi内置的唯一ObjectIdentity实现类。</p>
<p align=center><img height=123 src="http://book.csdn.net/BookFiles/310/img/image023.gif" width=165 border=0></p>
<p align=center>图14-3 &nbsp;Sid继承链</p>
<p>Acegi提供的ACL子系统正是围绕Acl、AccessControlEntry、Sid、ObjectIdentity展开的，这些对象存活于业务系统与RDBMS间。也正是这些接口的存在，我们才能够将Acegi提供的ACL子系统作用到任何RDBMS中。也就是说，Acegi提供的领域对象支持适合于所有的RDBMS、O/R Mapping技术。为了能够从RDBMS装载到相关对象，我们需要使用Acegi内置的如下AclService接口。</p>
<p>//获得ACL</p>
<p>public interface AclService {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //查找到parentIdentity的所有子ObjectIdentity，ACL管理工具经常要使用到它</p>
<p>&nbsp;&nbsp;&nbsp; public ObjectIdentity[] <strong>findChildren</strong>(ObjectIdentity parentIdentity);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //通过ObjectIdentity获得单个Acl对象</p>
<p>&nbsp;&nbsp;&nbsp; public Acl <strong>readAclById</strong>(ObjectIdentity object) throws NotFoundException;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //通过ObjectIdentity获得仅适合于sids的单个Acl对象</p>
<p>&nbsp;&nbsp;&nbsp; public Acl <strong>readAclById</strong>(ObjectIdentity object, Sid[] sids)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws NotFoundException;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //获得ObjectIdentity[]对应的Acl对象集合</p>
<p>&nbsp;&nbsp;&nbsp; public Map <strong>readAclsById</strong>(ObjectIdentity[] objects) throws NotFoundException;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //通过ObjectIdentity[]获得仅适合于sids的Acl对象集合</p>
<p>&nbsp;&nbsp;&nbsp; public Map <strong>readAclsById</strong>(ObjectIdentity[] objects, Sid[] sids)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws NotFoundException;</p>
<p>}</p>
<p>图14-4展示了Acegi内置的AclService继承链，处于这一继承链中的各个接口及实现类各有各的用途。比如，JdbcAclService用于实现AclService策略接口。</p>
<p align=center><img height=141 src="http://book.csdn.net/BookFiles/310/img/image024.gif" width=226 border=0></p>
<p align=center>图14-4 &nbsp;AclService继承链</p>
<p>MutableAclService用于维护Acl实例，开发者经常要同这一策略接口打交道，其定义如下。JdbcMutableAclService实现了MutableAclService。注意，开发者也可以提供O/R Mapping技术对应的AclService实现，比如Hibernate、JPA。Acegi仅仅实现了JDBC版本的AclService，开发者可以在自身的应用中同时使用O/R Mapping技术和JDBC。</p>
<p>//维护Acl实例</p>
<p>public interface MutableAclService extends <strong>AclService</strong> {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //在RDBMS中创建不含ACE的ACL，即Acl对象</p>
<p>&nbsp;&nbsp;&nbsp; public MutableAcl <strong>createAcl</strong>(ObjectIdentity objectIdentity)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws AlreadyExistsException;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //从RDBMS中删除objectIdentity对应的ACL</p>
<p>&nbsp;&nbsp;&nbsp; public void <strong>deleteAcl</strong>(ObjectIdentity objectIdentity, boolean deleteChildren)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws ChildrenExistException;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //将现有的acl实例同步到RDBMS中</p>
<p>&nbsp;&nbsp;&nbsp; public MutableAcl <strong>updateAcl</strong>(MutableAcl acl) throws NotFoundException;</p>
<p>}</p>
<p>通过本节内容，我们大致了解了Acegi提供的用于ACL子系统的各主要接口。本章后续内容将围绕这些接口展开论述。</p>
<h3><img height=38 src="http://book.csdn.net/BookFiles/310/img/image005.gif" width=67>14.1.3 &nbsp;ACL权限的定义</h3>
<p>我们可以对领域对象进行各种操作，比如新增、删除、修改、浏览、管理等。Acegi将各种ACL权限信息建模在BasePermission对象中，相关的ACL权限信息摘录如下。开发者经常需要同READ、WRITE、CREATE、DELETE、ADMINISTRATION等ACL权限打交道，这些权限的含义非常容易理解。借助于FieldRetrievingFactoryBean，开发者能够在DI容器中配置它们。</p>
<p>public static final Permission <strong>READ</strong> = </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new BasePermission(1 &lt;&lt; 0, 'R'); // 1</p>
<p>public static final Permission <strong>WRITE</strong> = </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new BasePermission(1 &lt;&lt; 1, 'W'); // 2</p>
<p>public static final Permission <strong>CREATE</strong> = </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new BasePermission(1 &lt;&lt; 2, 'C'); // 4</p>
<p>public static final Permission <strong>DELETE</strong> = </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new BasePermission(1 &lt;&lt; 3, 'D'); // 8</p>
<p>public static final Permission <strong>ADMINISTRATION</strong> = </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new BasePermission(1 &lt;&lt; 4, 'A'); // 16</p>
<p>图14-7展示了Acegi内置的Permission继承链。BasePermission将基本的权限建模好了，而借助于CumulativePermission，开发者能够建构复合权限。比如，我们可以对READ、WRITE等权限进行逻辑或运算，进而在RDBMS中存储复合权限，从而简化了ACL授权操作。</p>
<p align=center><img height=142 src="http://book.csdn.net/BookFiles/310/img/image025.gif" width=222 border=0></p>
<p align=center>图14-7 &nbsp;Permission继承链</p>
<p>下面摘录了ADMINISTRATION、READ、DELETE权限的定义示例，这些配置信息同样摘自于applicationContext-common-authorization.xml配置文件。各个AclEntryVoter投票器和AbstractAclProvider子类需要引用到这些ACL权限定义。</p>
<p>&lt;bean id="BasePermission.ADMINISTRATION" </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="staticField"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;org.acegisecurity.acls.domain.BasePermission.ADMINISTRATION&lt;/value&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&lt;/bean&gt;</p>
<p>&lt;bean id="BasePermission.READ" </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="staticField"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;org.acegisecurity.acls.domain.BasePermission.READ&lt;/value&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&lt;/bean&gt;</p>
<p>&lt;bean id="BasePermission.DELETE" </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="staticField"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;org.acegisecurity.acls.domain.BasePermission.DELETE&lt;/value&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&lt;/bean&gt;</p>
<!-- page -->
<img src ="http://www.blogjava.net/junky/aggbug/127937.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-07-03 19:24 <a href="http://www.blogjava.net/junky/archive/2007/07/03/127937.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>14.2  实施保护领域对象的重要步骤</title><link>http://www.blogjava.net/junky/archive/2007/07/03/127936.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Tue, 03 Jul 2007 11:21:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/07/03/127936.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/127936.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/07/03/127936.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/127936.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/127936.html</trackback:ping><description><![CDATA[<h3>14.2 &nbsp;实施保护领域对象的重要步骤</h3>
<p>在熟悉Acegi中ACL子系统的各个术语及基本概念后，我们需要通过实际企业应用验证它们，从而加深对它们的理解。</p>
<h3><img height=38 src="http://book.csdn.net/BookFiles/310/img/image005.gif" width=67>14.2.1&nbsp; RDBMS表的建立</h3>
<p>为了实施基于Acegi ACL领域对象授权支持的企业应用，开发者必须建立好同ACL相关的4张RDBMS表。它们的名字和含义如下。</p>
<p>l&nbsp; acl_class：用于存储领域对象对应的全限定名，比如sample.contact.Contact。</p>
<p>l&nbsp; acl_sid：用于存储ACL授权对象，或者是用户名，或者是角色名。比如，marissa用户、ROLE_USER角色。</p>
<p>l&nbsp; acl_object_identity：用于存储领域对象对应的Acl信息。</p>
<p>l&nbsp; acl_entry：用于存储Acl（acl_object_identity）对应的AccessControlEntry信息。默认时，acl_object_identity同acl_entry构成了主从表关系，并以1:<em>N</em>关系存在。</p>
<p>图14-8展示了这4张表间的具体关系，这是采用Hibernate Tools获得的图形化表示，此外，图中还展示了Acegi contacts示例存储领域对象的contacts表、存储用户和角色信息的users和authorities表。值得开发者注意的是，contacts、users、authorities表与ACL对应的4张表并无直接联系。接下来，我们来一一研究这些表的设计和内容。由于Acegi contacts示例采用了HSQLDB RDBMS，因此这些表对应的SQL DDL语句不能够直接用于其他生产数据库，比如Oracle 10g、SQL Server 2005。在领会Acegi ACL的思想后，开发者能够很轻松地给出特定数据库版本的ACL表定义。</p>
<p align=center><img height=269 src="http://book.csdn.net/BookFiles/310/img/image027.jpg" width=375 border=0></p>
<p align=center>图14-8 &nbsp;contacts示例设计的RDBMS表</p>
<p>其一，users表的定义如下：username列用于存储用户名，password列用于存储密码，enabled列用于存储启用标志位。</p>
<p>create table users(</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>username</strong> varchar_ignorecase(50) not null <strong>primary key</strong>,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>password</strong> varchar_ignorecase(50) not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>enabled</strong> boolean not null</p>
<p>);</p>
<p>图14-9给出了users表中已存储的示例数据。这里的password列的内容经过了MD5加密处理。</p>
<p align=center><img height=144 src="http://book.csdn.net/BookFiles/310/img/image028.jpg" width=289 border=0></p>
<p align=center>图14-9 &nbsp;users表已存储的示例数据</p>
<p>其二，authorities表的定义如下：username列用于存储用户名，authority列用于存储角色名。</p>
<p>create table authorities(</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>username</strong> varchar_ignorecase(50) not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>authority</strong> varchar_ignorecase(50) not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constraint fk_authorities_users foreign key(username) references users(username)</p>
<p>);</p>
<p>create unique index ix_auth_username on authorities(username,authority);</p>
<p>图14-10给出了authorities表中已存储的示例数据，其中的username列数据引用到users表中username列的数据。默认时，users和authorities表处于1:<em>N</em>的关系。</p>
<p align=center><img height=162 src="http://book.csdn.net/BookFiles/310/img/image029.jpg" width=161 border=0></p>
<p align=center>图14-10 &nbsp;authorities表已存储的示例数据</p>
<p>其三，contacts表的定义如下：id列用于标识领域对象，contact_name列存储联系人的姓名，而email列用于存储联系人的邮件地址。</p>
<p>create table contacts(</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>id</strong> bigint not null <strong>primary key</strong>, </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>contact_name</strong> varchar_ignorecase(50) not null, </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>email</strong> varchar_ignorecase(50) not null</p>
<p>);</p>
<p>图14-11给出了contacts表中已存储的示例数据。</p>
<p align=center><img height=145 src="http://book.csdn.net/BookFiles/310/img/image030.jpg" width=209 border=0></p>
<p align=center>图14-11 &nbsp;contacts表已存储的示例数据</p>
<p>在实际企业应用中，存在大量的领域对象，因而会存在大量的RDBMS表来存储它们。类似地，实际企业应用存储用户、角色信息的RDBMS表也不一定是users、authorities表，相信仔细阅读过本书相关章节的开发者都知道如何自定义它们。好了，再来研究4张ACL表的SQL DDL语句及相应的示例数据。</p>
<p>其四，acl_class表的定义如下：id列用于标识领域对象对应的全限定名，而class列用于存储领域对象对应的全限定名。</p>
<p>create table acl_class(</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>id</strong> bigint generated by default as identity(start with 100) not null <strong>primary key</strong>,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>class</strong> varchar_ignorecase(100) not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constraint unique_uk_2 unique(class)</p>
<p>);</p>
<p>图14-12给出了acl_class表中已存储的示例数据。由于Acegi contacts示例仅仅存在单个Contact领域对象类型，因此这一表仅仅存在单条记录。</p>
<p align=center><img height=38 src="http://book.csdn.net/BookFiles/310/img/image031.jpg" width=131 border=0></p>
<p align=center>图14-12 &nbsp;acl_class表已存储的示例数据</p>
<p>其五，acl_sid表的定义如下：principal列用于给定sid列的类型，即同一记录中存储了用户名还是角色名。相比之下，sid列存储用户名或者角色名。如果sid列存储了用户名，则principal列取值为true；如果sid列存储了角色名，则principal列取值为false。</p>
<p>create table acl_sid(</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>id</strong> bigint generated by default as identity(start with 100) not null <strong>primary key</strong>,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>principal</strong> boolean not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>sid</strong> varchar_ignorecase(100) not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constraint unique_uk_1 unique(sid,principal)</p>
<p>);</p>
<p>图14-13给出了acl_sid表中已存储的示例数据。由于sid列存储的数据都是用户名，因此principal列取值都为true。在某种程度上，acl_sid对应于Sid对象。</p>
<p align=center><img height=126 src="http://book.csdn.net/BookFiles/310/img/image032.jpg" width=119 border=0></p>
<p align=center>图14-13 &nbsp;acl_sid表已存储的示例数据</p>
<p>其六，acl_object_identity表的定义如下：id列为主键。其中，object_id_class引用到acl_class中的id列取值，图14-14中的object_id_class列取值都是100，即Contact领域对象类型。类似地，object_id_identity列的内容起到绑定特定领域对象的作用。比如，在contacts示例应用中，object_id_identity列的内容同contacts表中id列的内容保持一致。注意，开发者也可以通过其他策略维护object_id_identity列同特定领域对象间的绑定关系。默认时，我们建议领域对象都能够含有id属性，因为ObjectIdentityImpl可能会通过反射机制调用到领域对象的getId()方法，并将返回结果存储到object_id_identity列。最后，parent_object列用于指定当前ACL的父ACL，而own_sid列用于存储当前ACL的主人，entries_inheriting列用于指定当前ACL是否继承父ACL（含有的ACE集合）。可以看出，owner_sid列取值引用到acl_sid中id列取值。</p>
<p>create table acl_object_identity(</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>id</strong> bigint generated by default as identity(start with 100) not null <strong>primary key</strong>,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>object_id_class</strong> bigint not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>object_id_identity</strong> bigint not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>parent_object</strong> bigint,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>owner_sid</strong> bigint,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>entries_inheriting</strong> boolean not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constraint unique_uk_3 unique(object_id_class,object_id_identity),</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constraint foreign_fk_1 foreign key(parent_object)references acl_object_identity(id),</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constraint foreign_fk_2 foreign key(object_id_class)references acl_class(id),</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constraint foreign_fk_3 foreign key(owner_sid)references acl_sid(id)</p>
<p>);</p>
<p>图14-14给出了RDBMS acl_object_identity表中已存储的示例数据。在某种程度上，acl_object_identity对应于Acl对象。</p>
<p align=center><img height=145 src="http://book.csdn.net/BookFiles/310/img/image033.jpg" width=482 border=0></p>
<p align=center>图14-14 &nbsp;acl_object_identity表已存储的示例数据</p>
<p>其七，acl_entry表的定义如下：id列是主键。其中，acl_object_identity表指定当前ACE所属的ACL，即对应到acl_object_identity表中的id列。类似地，sid列用于存储当前ACE所属的授权对象，它对应于acl_sid表中的id列；ace_order列对ACE集合进行排序；mask列存储ACE权限信息，具体情况请参考BasePermission和CumulativePermission类；granting列表明当前的ACE权限是否已经授给了同一记录持有的sid列（用户名或角色）；audit_success和audit_failure列用于审计目的，供各种基于Acegi ACL的管理工具在审计ACL（ACE）时使用。开发者可以借助于Acegi暴露的API开发出相应的审计（管理）工具。</p>
<p>create table acl_entry(</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>id</strong> bigint generated by default as identity(start with 100) not null<strong> primary key</strong>,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>acl_object_identity</strong> bigint not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>ace_order</strong> int not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>sid</strong> bigint not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>mask</strong> integer not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>granting</strong> boolean not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>audit_success</strong> boolean not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>audit_failure</strong> boolean not null,</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constraint unique_uk_4 unique(acl_object_identity,ace_order),</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constraint foreign_fk_4 foreign key(acl_object_identity) references acl_object_identity(id),</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constraint foreign_fk_5 foreign key(sid) references acl_sid(id)</p>
<p>);</p>
<p>图14-15给出了RDBMS acl_entry表中已存储的示例数据。在某种程度上，acl_entry对应于AccessControlEntry对象。</p>
<p align=center><img height=122 src="http://book.csdn.net/BookFiles/310/img/image034.jpg" width=406 border=0></p>
<p align=center>图14-15&nbsp; acl_entry表已存储的示例数据</p>
<!-- page -->
<img src ="http://www.blogjava.net/junky/aggbug/127936.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-07-03 19:21 <a href="http://www.blogjava.net/junky/archive/2007/07/03/127936.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi Security -- Spring下最优秀的安全系统(转)</title><link>http://www.blogjava.net/junky/archive/2007/07/03/127915.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Tue, 03 Jul 2007 09:40:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/07/03/127915.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/127915.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/07/03/127915.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/127915.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/127915.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 一&nbsp;Acegi安全系统介绍&nbsp;&nbsp;&nbsp; Author: cac 差沙 &nbsp;&nbsp;&nbsp; Acegi是Spring Framework 下最成熟的安全系统，它提供了强大灵活的企业级安全服务，如完善的认证和授权机制，Http资源访问控制，Method 调用访问控制，Access Control List (ACL) 基于对象实例的访问控制，...&nbsp;&nbsp;<a href='http://www.blogjava.net/junky/archive/2007/07/03/127915.html'>阅读全文</a><img src ="http://www.blogjava.net/junky/aggbug/127915.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-07-03 17:40 <a href="http://www.blogjava.net/junky/archive/2007/07/03/127915.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Acegi中使用ACL(转)</title><link>http://www.blogjava.net/junky/archive/2007/07/03/127816.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Tue, 03 Jul 2007 05:11:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/07/03/127816.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/127816.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/07/03/127816.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/127816.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/127816.html</trackback:ping><description><![CDATA[Acegi好早就实现了ACL（好像是0.5），但是使用起来确实有点麻烦，所以用的不是太广泛。这里简单的说明一下使用方法，希望有更多的朋友来试试。<br><br>首先要理解Acegi里面Voter的概念，ACL正是在一个Voter上扩展起来的。现来看一下AclVoter的配置。<br><br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean&nbsp;</span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="aclBeanReadVoter"</span><span style="COLOR: #ff0000">&nbsp;class</span><span style="COLOR: #0000ff">="org.acegisecurity.vote.BasicAclEntryVoter"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property&nbsp;</span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="processConfigAttribute"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">value</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">ACL_READ</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">value</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property&nbsp;</span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="processDomainObjectClass"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">value</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">org.springside.modules.security.acl.domain.AclDomainAware</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">value</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property&nbsp;</span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="aclManager"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref&nbsp;</span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="aclManager"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property&nbsp;</span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="requirePermission"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref&nbsp;</span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref&nbsp;</span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="org.acegisecurity.acl.basic.SimpleAclEntry.READ"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span></div>
<ol>
    <li><span style="COLOR: #000000">ACL_READ指的是这个Voter对哪些SecurityConfig起作用，我们可以把<span style="COLOR: #000000">ACL_READ配置在想要拦截的Method上。比方说我们要拦截readOrder这个方法，以实现ACL控制，可以这样配置。<br><em><font color=#006400>orderManager.readOrder=ACL_READ</font></em></span></span>
    <li><span style="COLOR: #000000"><span style="COLOR: #000000"><font color=#000000>processDomainObjectClass指出哪些DomainObject是要进行ACL校验的。</font></span></span>
    <li><span style="COLOR: #000000"><span style="COLOR: #000000"><font color=#000000>aclManager是一个比较重要的概念，主要负责在权限列表中根据用户和DomainObject取得acl列表。</font></span></span>
    <li><span style="COLOR: #000000"><span style="COLOR: #000000">requirePermission指出要进行这个操作必须具备的acl权限，比方说read操作就必须有<font color=#000000>ADMINISTRATION或READ两个权限。</font></span></span></li>
</ol>
<p><span style="COLOR: #000000"><span style="COLOR: #000000">其实整个过程看下来比较清晰，下面来看一下AclManager如何配置。<br></span></span></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">&lt;!--</span><span style="COLOR: #008000">&nbsp;=========&nbsp;ACCESS&nbsp;CONTROL&nbsp;LIST&nbsp;LOOKUP&nbsp;MANAGER&nbsp;DEFINITIONS&nbsp;=========&nbsp;</span><span style="COLOR: #008000">--&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean&nbsp;</span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="aclManager"</span><span style="COLOR: #ff0000">&nbsp;class</span><span style="COLOR: #0000ff">="org.acegisecurity.acl.AclProviderManager"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property&nbsp;</span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="providers"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref&nbsp;</span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="basicAclProvider"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean&nbsp;</span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="basicAclProvider"</span><span style="COLOR: #ff0000">&nbsp;class</span><span style="COLOR: #0000ff">="org.acegisecurity.acl.basic.BasicAclProvider"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property&nbsp;</span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="basicAclDao"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref&nbsp;</span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="basicAclExtendedDao"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean&nbsp;</span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="basicAclExtendedDao"</span><span style="COLOR: #ff0000">&nbsp;class</span><span style="COLOR: #0000ff">="org.acegisecurity.acl.basic.jdbc.JdbcExtendedDaoImpl"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property&nbsp;</span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="dataSource"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref&nbsp;</span><span style="COLOR: #ff0000">bean</span><span style="COLOR: #0000ff">="dataSource"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span></div>
<br>很明显ACLManager继承了Acegi的一贯风格，Provider可以提供多种取得ACL访问列表的途径，默认的是用<font color=#0000ff>basicAclProvider</font><font color=#000000>在数据库中取得。既然提到了数据库，那我们就来看一下Acegi默认提供的ACL在数据库里的保存表结构：<br><br>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">CREATE</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">TABLE</span><span style="COLOR: #000000">&nbsp;acl_object_identity&nbsp;(<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>id&nbsp;</span><span style="COLOR: #ff00ff">IDENTITY</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #808080">NOT</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">NULL</span><span style="COLOR: #000000">,<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>object_identity&nbsp;VARCHAR_IGNORECASE(</span><span style="FONT-WEIGHT: bold; COLOR: #800000">250</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #808080">NOT</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">NULL</span><span style="COLOR: #000000">,<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>parent_object&nbsp;</span><span style="FONT-WEIGHT: bold; COLOR: #000000">INTEGER</span><span style="COLOR: #000000">,<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>acl_class&nbsp;VARCHAR_IGNORECASE(</span><span style="FONT-WEIGHT: bold; COLOR: #800000">250</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #808080">NOT</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">NULL</span><span style="COLOR: #000000">,<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">CONSTRAINT</span><span style="COLOR: #000000">&nbsp;unique_object_identity&nbsp;</span><span style="COLOR: #0000ff">UNIQUE</span><span style="COLOR: #000000">(object_identity),<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">FOREIGN</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">KEY</span><span style="COLOR: #000000">&nbsp;(parent_object)&nbsp;</span><span style="COLOR: #0000ff">REFERENCES</span><span style="COLOR: #000000">&nbsp;acl_object_identity(id)<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>);<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">CREATE</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">TABLE</span><span style="COLOR: #000000">&nbsp;acl_permission&nbsp;(<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>id&nbsp;</span><span style="COLOR: #ff00ff">IDENTITY</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #808080">NOT</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">NULL</span><span style="COLOR: #000000">,<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>acl_object_identity&nbsp;</span><span style="FONT-WEIGHT: bold; COLOR: #000000">INTEGER</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #808080">NOT</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">NULL</span><span style="COLOR: #000000">,<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>recipient&nbsp;VARCHAR_IGNORECASE(</span><span style="FONT-WEIGHT: bold; COLOR: #800000">100</span><span style="COLOR: #000000">)&nbsp;</span><span style="COLOR: #808080">NOT</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">NULL</span><span style="COLOR: #000000">,<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>mask&nbsp;</span><span style="FONT-WEIGHT: bold; COLOR: #000000">INTEGER</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #808080">NOT</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">NULL</span><span style="COLOR: #000000">,<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">CONSTRAINT</span><span style="COLOR: #000000">&nbsp;unique_recipient&nbsp;</span><span style="COLOR: #0000ff">UNIQUE</span><span style="COLOR: #000000">(acl_object_identity,&nbsp;recipient),<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">FOREIGN</span><span style="COLOR: #000000">&nbsp;</span><span style="COLOR: #0000ff">KEY</span><span style="COLOR: #000000">&nbsp;(acl_object_identity)&nbsp;</span><span style="COLOR: #0000ff">REFERENCES</span><span style="COLOR: #000000">&nbsp;acl_object_identity(id)<br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>);</span></div>
</font>
<ol>
    <li>acl_object_identity表存放了所有受保护的domainObject的信息。其中object_identity字段保存了domainObject的class和id，默认的保存格式是：domainClass:domainObjectId。
    <li>acl_permission&nbsp;就是ACL权限列表了，recipient&nbsp;是用户或角色信息，mask表示了这个用户或角色对这个domainObject的访问权限。注意这些信息的保存格式都是可以根据自己的需要改变的。</li>
</ol>
<p>这样读取和删除的时候Acegi就能很好的完成拦截工作，但是读取一个List的时候，如何才能把该用户不能操作的domainObject剔除掉呢？这就需要afterInvocationManager来完成这个工作。下面来看下配置：<br></p>
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">&lt;!--</span><span style="COLOR: #008000">&nbsp;==============&nbsp;"AFTER&nbsp;INTERCEPTION"&nbsp;AUTHORIZATION&nbsp;DEFINITIONS&nbsp;===========&nbsp;</span><span style="COLOR: #008000">--&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean&nbsp;</span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="afterInvocationManager"</span><span style="COLOR: #ff0000">&nbsp;class</span><span style="COLOR: #0000ff">="org.acegisecurity.afterinvocation.AfterInvocationProviderManager"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property&nbsp;</span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="providers"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref&nbsp;</span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="afterAclCollectionRead"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">&lt;!--</span><span style="COLOR: #008000">&nbsp;Processes&nbsp;AFTER_ACL_COLLECTION_READ&nbsp;configuration&nbsp;settings&nbsp;</span><span style="COLOR: #008000">--&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean&nbsp;</span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="afterAclCollectionRead"</span><span style="COLOR: #ff0000">&nbsp;class</span><span style="COLOR: #0000ff">="org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationCollectionFilteringProvider"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property&nbsp;</span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="aclManager"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref&nbsp;</span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="aclManager"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property&nbsp;</span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="requirePermission"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref&nbsp;</span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref&nbsp;</span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="org.acegisecurity.acl.basic.SimpleAclEntry.READ"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><img src="http://www.blogjava.net/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span></div>
<br><font color=#000000>afterAclCollectionRead会在拦截的方法执行结束的时候执行。主要的作用就是在返回的List中挨个检查domainObject的操作权限，然后根据requirePermission来剔除不符合的domainObject。</font> 
<img src ="http://www.blogjava.net/junky/aggbug/127816.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-07-03 13:11 <a href="http://www.blogjava.net/junky/archive/2007/07/03/127816.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi ACL使用说明</title><link>http://www.blogjava.net/junky/archive/2007/07/03/127764.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Tue, 03 Jul 2007 02:39:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/07/03/127764.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/127764.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/07/03/127764.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/127764.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/127764.html</trackback:ping><description><![CDATA[本文假设你对Acegi其他部分已经比较熟悉。Acegi 的ACL控制是建立在对相应业务方法拦截的基础上的。这里以Acegi自带的contacts例子来说明。<strong><br>先看看总的配置</strong>：<br>
<div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">bean </span><span style="COLOR: rgb(255,0,0)">id</span><span style="COLOR: rgb(0,0,255)">="contactManagerSecurity"</span><span style="COLOR: rgb(255,0,0)"> class</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="authenticationManager"</span><span style="COLOR: rgb(0,0,255)">&gt;&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">bean</span><span style="COLOR: rgb(0,0,255)">="authenticationManager"</span><span style="COLOR: rgb(0,0,255)">/&gt;&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="accessDecisionManager"</span><span style="COLOR: rgb(0,0,255)">&gt;&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="businessAccessDecisionManager"</span><span style="COLOR: rgb(0,0,255)">/&gt;&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="afterInvocationManager"</span><span style="COLOR: rgb(0,0,255)">&gt;&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="afterInvocationManager"</span><span style="COLOR: rgb(0,0,255)">/&gt;&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="objectDefinitionSource"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">value</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample.contact.ContactManager.getAll=AFTER_ACL_COLLECTION_READ<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample.contact.ContactManager.getById=AFTER_ACL_READ<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample.contact.ContactManager.delete=ACL_CONTACT_DELETE<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample.contact.ContactManager.deletePermission=ACL_CONTACT_ADMIN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample.contact.ContactManager.addPermission=ACL_CONTACT_ADMIN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">value</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">bean</span><span style="COLOR: rgb(0,0,255)">&gt;</span></div>
该拦截器实现了org.aopalliance.intercept.MethodInterceptor接口。在方法被调用之前，拦截器会先调用 AuthenticationManager判断用户身份是否已验证，然后从objectDefinitionSource中获取方法所对应的权限，再调用AccessDecisionManager来匹配用户权限和方法对应的权限。如果用户没有足够权限调用当前方法，则抛出 AccessDeniedException使方法不能被调用。方法调用后会调用AfterInvocationManager对返回的结果进行再次处理。下面依次说明。<br><strong>AccessDecisionManager的配置</strong>：<br>
<div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">bean </span><span style="COLOR: rgb(255,0,0)">id</span><span style="COLOR: rgb(0,0,255)">="businessAccessDecisionManager"</span><span style="COLOR: rgb(255,0,0)"> class</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.vote.AffirmativeBased"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="allowIfAllAbstainDecisions"</span><span style="COLOR: rgb(0,0,255)">&gt;&lt;</span><span style="COLOR: rgb(128,0,0)">value</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)">false</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">value</span><span style="COLOR: rgb(0,0,255)">&gt;&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="decisionVoters"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">list</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="roleVoter"</span><span style="COLOR: rgb(0,0,255)">/&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="aclContactReadVoter"</span><span style="COLOR: rgb(0,0,255)">/&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="aclContactDeleteVoter"</span><span style="COLOR: rgb(0,0,255)">/&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="aclContactAdminVoter"</span><span style="COLOR: rgb(0,0,255)">/&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">list</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">bean</span><span style="COLOR: rgb(0,0,255)">&gt;</span></div>
AccessDecisionManager 接口有decide()和support()方法。decide()方法决策是否批准通过即方法是否容许调用，如果没抛出 AccessDeniedException则为允许访问资源，否则拒绝访问。support()方法是根据配置属性和受保护资源的类来判断是否需要对该资源作出决策判断。<br>AccessDecisionManager的 decisionVoters属性需要一个或多个Voter(投票者)，Voter必须实现AccessDecisionVoter 接口。Voter的工作是去匹配用户已拥有的权限和受保护的资源要求的权限，在该资源有相应权限的情况下，如果匹配则投允许票，否则投反对票。 <br>allowIfAllAbstainDecisions属性表示是否允许所有都弃权时就通过。Voter的实现类RoleVoter在当受保护资源的名字由ROLE_开始时才参与投票。<br>AccessDecisionManager有三个实现类，功能各不相同:<br>AffirmativeBased: 当至少有一个Voter投允许票时才通过<br>UnanimousBased: 没有Voter投反对票时才通过<br>ConsensusBased: 当所有Voter都投允许票时才通过<br><strong>下面列出一个Voter的配置</strong>：<br>
<div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">bean </span><span style="COLOR: rgb(255,0,0)">id</span><span style="COLOR: rgb(0,0,255)">="aclContactDeleteVoter"</span><span style="COLOR: rgb(255,0,0)"> class</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.vote.BasicAclEntryVoter"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="processConfigAttribute"</span><span style="COLOR: rgb(0,0,255)">&gt;&lt;</span><span style="COLOR: rgb(128,0,0)">value</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)">ACL_CONTACT_DELETE</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">value</span><span style="COLOR: rgb(0,0,255)">&gt;&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="processDomainObjectClass"</span><span style="COLOR: rgb(0,0,255)">&gt;&lt;</span><span style="COLOR: rgb(128,0,0)">value</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)">sample.contact.Contact</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">value</span><span style="COLOR: rgb(0,0,255)">&gt;&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="aclManager"</span><span style="COLOR: rgb(0,0,255)">&gt;&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="aclManager"</span><span style="COLOR: rgb(0,0,255)">/&gt;&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="requirePermission"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">list</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"</span><span style="COLOR: rgb(0,0,255)">/&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.acl.basic.SimpleAclEntry.DELETE"</span><span style="COLOR: rgb(0,0,255)">/&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">list</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">bean</span><span style="COLOR: rgb(0,0,255)">&gt;</span></div>
上面第一个配置里有这么一行：sample.contact.ContactManager.delete=ACL_CONTACT_DELETE<br>所以在调用sample.contact.ContactManager.delete这个方法时aclContactDeleteVoter会参与投票，它会获得sample.contact.Contact这个对象（这个对象从delete方法的参数中获得），然后通过aclManager去获得当前用户对该Contact实例的ACL权限，最后拿这个权限与我们需要的权限比对，我们配置需要的权限是 org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION和 org.acegisecurity.acl.basic.SimpleAclEntry.DELETE。如果我们通过aclManager获得的权限包括这两个配置的权限之一，Voter就投容许票，方法容许调用。如果不包括，那对不起，反对票，AccessDecisionManager就会抛出 AccessDeniedException。方法拒绝调用。<br><strong>AclManager的配置</strong>：<br>
<div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">bean </span><span style="COLOR: rgb(255,0,0)">id</span><span style="COLOR: rgb(0,0,255)">="aclManager"</span><span style="COLOR: rgb(255,0,0)"> class</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.acl.AclProviderManager"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="providers"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">list</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="basicAclProvider"</span><span style="COLOR: rgb(0,0,255)">/&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">list</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">bean</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">bean </span><span style="COLOR: rgb(255,0,0)">id</span><span style="COLOR: rgb(0,0,255)">="basicAclProvider"</span><span style="COLOR: rgb(255,0,0)"> class</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.acl.basic.BasicAclProvider"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="basicAclDao"</span><span style="COLOR: rgb(0,0,255)">&gt;&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="basicAclExtendedDao"</span><span style="COLOR: rgb(0,0,255)">/&gt;&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">bean</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">bean </span><span style="COLOR: rgb(255,0,0)">id</span><span style="COLOR: rgb(0,0,255)">="basicAclExtendedDao"</span><span style="COLOR: rgb(255,0,0)"> class</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.acl.basic.jdbc.JdbcExtendedDaoImpl"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="dataSource"</span><span style="COLOR: rgb(0,0,255)">&gt;&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">bean</span><span style="COLOR: rgb(0,0,255)">="dataSource"</span><span style="COLOR: rgb(0,0,255)">/&gt;&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">bean</span><span style="COLOR: rgb(0,0,255)">&gt;</span></div>
AclManager是整个ACL中一个很核心的概念，它包含了两个方法AclEntry[] getAcls(Object domainInstance)和<br>AclEntry[] getAcls(Object domainInstance, Authentication authentication)。在了解这两个方法前，我们先了解AclEntry这个对象。AclEntry只是一个接口，系统中一般都是造型为 BasicAclEntry。它包括了这个Entry所保护的domainObject instance（这里是Contact），实际它实现上是以AclObjectIdentity来替代这个domainObject的（domainClass+domainObjectId）；它包括了谁（Recipient）拥有这个domainObject instance以及他所对这个domainObject instance的操作权限（mask）。<br>一个domainObject instance对应了多个AclEntry，比如一条通讯录张三可以查看，而李四可以管理，一个Contact instance就对应了两个AclEntry，第一个AclEntry包含信息：所保护的domainObject（Contact）,谁（张三），权限（查看）;第二个AclEntry包含信息：所保护的domainObject（Contact）,谁（李四），权限（管理）。<br>这样AclManager的两个方法就很好理解了getAcls(Object domainInstance)返回所有这个domainInstance所对应的权限信息，<br>getAcls(Object domainInstance, Authentication authentication)在第一个方法返回结果的基础上做了过滤，过滤出和authentication（当前用户）相关的权限信息。如果当前用户是张三，则返回与张三对应的记录。<br><br>这样Acegi就会拦截业务方法发挥相应的作用，但是在业务方法返回一个List或是单个 domainObject instance的时候，同样也是需要把用户没有权限查看的domainObject instance过滤掉的，这时就要用afterInvocationManager了，<strong>看配置</strong>：<br>
<div style="BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: rgb(204,204,204) 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: rgb(204,204,204) 1px solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: rgb(204,204,204) 1px solid; BACKGROUND-COLOR: rgb(238,238,238)"><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">bean </span><span style="COLOR: rgb(255,0,0)">id</span><span style="COLOR: rgb(0,0,255)">="afterInvocationManager"</span><span style="COLOR: rgb(255,0,0)"> class</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.afterinvocation.AfterInvocationProviderManager"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="providers"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">list</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="afterAclRead"</span><span style="COLOR: rgb(0,0,255)">/&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="afterAclCollectionRead"</span><span style="COLOR: rgb(0,0,255)">/&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">list</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">bean</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,128,0)">&lt;!--</span><span style="COLOR: rgb(0,128,0)"> Processes AFTER_ACL_COLLECTION_READ configuration settings </span><span style="COLOR: rgb(0,128,0)">--&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">bean </span><span style="COLOR: rgb(255,0,0)">id</span><span style="COLOR: rgb(0,0,255)">="afterAclCollectionRead"</span><span style="COLOR: rgb(255,0,0)"> class</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationCollectionFilteringProvider"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="aclManager"</span><span style="COLOR: rgb(0,0,255)">&gt;&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="aclManager"</span><span style="COLOR: rgb(0,0,255)">/&gt;&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="requirePermission"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">list</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"</span><span style="COLOR: rgb(0,0,255)">/&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.acl.basic.SimpleAclEntry.READ"</span><span style="COLOR: rgb(0,0,255)">/&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">list</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">bean</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,128,0)">&lt;!--</span><span style="COLOR: rgb(0,128,0)"> Processes AFTER_ACL_READ configuration settings </span><span style="COLOR: rgb(0,128,0)">--&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">bean </span><span style="COLOR: rgb(255,0,0)">id</span><span style="COLOR: rgb(0,0,255)">="afterAclRead"</span><span style="COLOR: rgb(255,0,0)"> class</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationProvider"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="aclManager"</span><span style="COLOR: rgb(0,0,255)">&gt;&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="aclManager"</span><span style="COLOR: rgb(0,0,255)">/&gt;&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">property </span><span style="COLOR: rgb(255,0,0)">name</span><span style="COLOR: rgb(0,0,255)">="requirePermission"</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">list</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"</span><span style="COLOR: rgb(0,0,255)">/&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;</span><span style="COLOR: rgb(128,0,0)">ref </span><span style="COLOR: rgb(255,0,0)">local</span><span style="COLOR: rgb(0,0,255)">="org.acegisecurity.acl.basic.SimpleAclEntry.READ"</span><span style="COLOR: rgb(0,0,255)">/&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">list</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">property</span><span style="COLOR: rgb(0,0,255)">&gt;</span><span style="COLOR: rgb(0,0,0)"><br>&nbsp;&nbsp;&nbsp;</span><span style="COLOR: rgb(0,0,255)">&lt;/</span><span style="COLOR: rgb(128,0,0)">bean</span><span style="COLOR: rgb(0,0,255)">&gt;</span></div>
afterAclCollectionRead 会对配置AFTER_ACL_COLLECTION_READ的方法进行拦截，这里是 sample.contact.ContactManager.getAll方法，它会遍历方法返回的domainObject，然后挨个通过 aclManager判断当前用户对domainObject的权限，如果和需要的权限不和，则过滤掉。呵呵，传说中的虎牙子就在此时产生了！<br>afterAclRead则依次类推。<br>参考了ss wiki里相关的文档，特别是差沙和cac的文档，写的相当好。另外acegi的代码也是相当的易读。
<img src ="http://www.blogjava.net/junky/aggbug/127764.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-07-03 10:39 <a href="http://www.blogjava.net/junky/archive/2007/07/03/127764.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi简介</title><link>http://www.blogjava.net/junky/archive/2007/06/28/126773.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Thu, 28 Jun 2007 04:44:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/06/28/126773.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/126773.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/06/28/126773.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/126773.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/126773.html</trackback:ping><description><![CDATA[Acegi安全系统，是一个用于Spring Framework的安全框架，能够和目前流行的Web容器无缝集成。它使用了Spring的方式提供了安全和认证安全服务，包括使用Bean Context，拦截器和面向接口的编程方式。因此，Acegi安全系统能够轻松地适用于复杂的安全需求。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 安全涉及到两个不同的概念，认证和授权。前者是关于确认用户是否确实是他们所宣称的身份。授权则是关于确认用户是否有允许执行一个特定的操作。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在Acegi安全系统中，需要被认证的用户，系统或代理称为"Principal"。Acegi安全系统和其他的安全系统不同，它并没有角色和用户组的概念。<br>Acegi系统设计<br>&nbsp;&nbsp;关键组件<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Acegi安全系统包含以下七个关键的功能组件：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1 Authentication对象，包含了Principal，Credential和Principal的授权信息。同时还可以包含关于发起认证请求的客户的其他信息，如IP地址。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 ContextHolder对象，使用ThreadLocal储存Authentication对象的地方。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3 AuthenticationManager，用于认证ContextHolder中的Authentication对象。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 AccessDecissionManager，用于授权一个特定的操作。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5 RunAsManager，当执行特定的操作时，用于选择性地替换Authentication对象。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6 Secure Object拦截器，用于协调AuthenticationManager，AccessDecissionManager，RunAsManager和特定操作的执行。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;7 ObjectDefinitionSource，包含了特定操作的授权定义。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这七个关键的功能组件的关系如下图所示（图中灰色部分是关键组件）：<br><br><br><strong>安全管理对象</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi安全系统目前支持两类安全管理对象。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第一类的安全管理对象管理AOP Alliance的MethodInvocation，开发人员可以用它来保护Spring容器中的业务对象。为了使Spring管理的Bean可以作为MethodInvocation来使用，Bean可以通过ProxyFactoryBean和BeanNameAutoProxyCreator来管理，就像在Spring的事务管理一样使用。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第二类是FilterInvocation。它用过滤器（Filter）来创建，并简单地包装了HTTP的ServletRequest，ServletResponse和FilterChain。FilterInvocation可以用来保护HTTP资源。通常，开发人员并不需要了解它的工作机制，因为他们只需要将Filter加入web.xml，Acegi安全系统就可以工作了。<br><br><strong>安全配置参数</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 每个安全管理对象都可以描述数量不限的各种安全认证请求。例如，MethodInvocation对象可以描述带有任意参数的任意方法的调用，而FilterInvocation可以描述任意的HTTP URL。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi安全系统需要记录应用于每个认证请求的安全配置参数。例如，对于BankManager.getBalance（int accountNumber）方法和BankManager.approveLoan（int applicationNumber）方法，它们需要的认证请求的安全配置很不相同。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了保存不同的认证请求的安全配置，需要使用配置参数。从实现的视角来看，配置参数使用ConfigAttribute接口来表示。Acegi安全系统提供了ConfigAttribute接口的一个实现，SecurityConfig，它把配置参数保存为一个字符串。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ConfigAttributeDefinition类是ConfigAttribute对象的一个简单的容器，它保存了和特定请求相关的ConfigAttribute的集合。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当安全拦截器收到一个安全认证请求时，需要决定应用哪一个配置参数。换句话说，它需要找出应用于这个请求的ConfigAttributeDefinition对象。这个查找的过程是由ObjectDefinitionSource接口来处理的。这个接口的主要方法是public ConfigAttributeDefinition getAttributes(Object object)，其中Object参数是一个安全管理对象。因为安全管理对象包含有认证请求的详细信息，所以ObjectDefinitionSource接口的实现类可以从中获得所需的详细信息，以查找相关的ConfigAttributeDefiniton对象。<br><br><br><strong>Acegi如何工作</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为了说明Acegi安全系统如何工作，我们设想一个使用Acegi的例子。通常，一个安全系统需要发挥作用，它必须完成以下的工作：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1 首先，系统从客户端请求中获得Principal和Credential；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2 然后系统认证Principal和Credential信息；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3 如果认证通过，系统取出Principal的授权信息；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 接下来，客户端发起操作请求；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5 系统根据预先配置的参数检查Principal对于该操作的授权；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;6 如果授权检查通过则执行操作，否则拒绝。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;那么，Acegi安全系统是如何完成这些工作的呢？首先，我们来看看Acegi安全系统的认证和授权的相关类： <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;安全拦截器的抽象基类，它包含有两个管理类，AuthenticationManager和AccessDecisionManager。AuthenticationManager用于认证ContextHolder中的Authentication对象（包含了Principal，Credential和Principal的授权信息）；AccessDecissionManager则用于授权一个特定的操作。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面来看一个MethodSecurityInterceptor的例子：<br>
<pre class=overflow>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean id="bankManagerSecurity" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="validateConfigAttributes"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;true&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="authenticationManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="authenticationManager"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="accessDecisionManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="accessDecisionManager"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="objectDefinitionSource"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; net.sf.acegisecurity.context.BankManager.delete*=<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROLE_SUPERVISOR,RUN_AS_SERVER<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; net.sf.acegisecurity.context.BankManager.getBalance=<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ROLE_TELLER,ROLE_SUPERVISOR,BANKSECURITY_CUSTOMER,RUN_<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt; </pre>
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上面的配置文件中，MethodSecurityInterceptor是AbstractSecurityInterceptor的一个实现类。它包含了两个管理器，authenticationManager和accessDecisionManager。这两者的配置如下：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<pre class=overflow>&lt;bean id="authenticationDao" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="dataSource"&gt;&lt;ref bean="dataSource"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean id="daoAuthenticationProvider" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationDao"&gt;&lt;ref bean="authenticationDao"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="providers"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt;&lt;ref bean="daoAuthenticationProvider"/&gt;&lt;/list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="allowIfAllAbstainDecisions"&gt;&lt;value&gt;false&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="decisionVoters"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt;&lt;ref bean="roleVoter"/&gt;&lt;/list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/bean&gt;</pre>
<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 准备工作做好了，现在我们来看看Acegi安全系统是如何实现认证和授权机制的。以使用HTTP BASIC认证的应用为例子，它包括下面的步骤：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1. 用户登录系统，Acegi从acegisecurity.ui子系统的安全拦截器（如BasicProcessingFilter）中得到用户的登录信息（包括Principal和Credential）并放入Authentication对象，并保存在ContextHolder对象中；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2. 安全拦截器将Authentication对象交给AuthenticationManager进行身份认证，如果认证通过，返回带有Principal授权信息的Authentication对象。此时ContextHolder对象的Authentication对象已拥有Principal的详细信息；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3. 用户登录成功后，继续进行业务操作；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4. 安全拦截器（bankManagerSecurity）收到客户端操作请求后，将操作请求的数据包装成安全管理对象（FilterInvocation或MethodInvocation对象）；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 5. 然后，从配置文件（ObjectDefinitionSource）中读出相关的安全配置参数ConfigAttributeDefinition；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6. 接着，安全拦截器取出ContextHolder中的Authentication对象，把它传递给AuthenticationManager进行身份认证，并用返回值更新ContextHolder的Authentication对象；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 7. 将Authentication对象，ConfigAttributeDefinition对象和安全管理对象（secure Object）交给AccessDecisionManager，检查Principal的操作授权；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8. 如果授权检查通过则执行客户端请求的操作，否则拒绝；<br><br><strong>AccessDecisionVoter</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注意上节的accessDecisionManager是一个AffirmativeBased类，它对于用户授权的投票策略是，只要通过其中的一个授权投票检查，即可通过；它的allowIfAllAbstainDecisions属性值是false，意思是如果所有的授权投票是都是弃权，则通不过授权检查。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi安全系统包括了几个基于投票策略的AccessDecisionManager，上节的RoleVoter就是其中的一个投票策略实现，它是AccessDecisionVoter的一个子类。AccessDecisionVoter的具体实现类通过投票来进行授权决策，AccessDecisionManager则根据投票结果来决定是通过授权检查，还是抛出AccessDeniedException例外。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AccessDecisionVoter接口共有三个方法：<br>public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config);<br>public boolean supports(ConfigAttribute attribute);<br>public boolean supports(Class clazz);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其中的vote方法返回int返回值，它们是AccessDecisionVoter的三个静态成员属性：ACCESS_ABSTAIN,，ACCESS_DENIED和ACCESS_GRANTED，它们分别是弃权，否决和赞成。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Acegi安全系统中，使用投票策略的AccessDecisionManager共有三个具体实现类：AffirmativeBased、ConsensusBased和UnanimousBased。它们的投票策略是，AffirmativeBased类只需有一个投票赞成即可通过；ConsensusBased类需要大多数投票赞成即可通过；而UnanimousBased类需要所有的投票赞成才能通过。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RoleVoter类是一个Acegi安全系统AccessDecisionVoter接口的实现。如果ConfigAttribute以ROLE_开头，RoleVoter则进行投票。如果GrantedAuthority的getAutority方法的String返回值匹配一个或多个以ROLE_开头的ConfigAttribute，则投票通过，否则不通过。如果没有以ROLE_开头的ConfigAttribute，RoleVoter则弃权。<br><br><strong>安全拦截器</strong><br>&nbsp;&nbsp;拦截器如何工作<br>&nbsp;&nbsp;MethodInvocation拦截器<br>&nbsp;&nbsp;FilterInvocation拦截器<br>认证<br>&nbsp;&nbsp;认证请求<br>&nbsp;&nbsp;认证管理器<br>&nbsp;&nbsp;Authentication Provider<br>授权<br>&nbsp;&nbsp;Access Decision Manager<br>&nbsp;&nbsp;Voting Decision Manager<br>&nbsp;&nbsp;授权管理推荐<br>ContextHolder的用户接口<br>&nbsp;&nbsp;用户接口目标<br>&nbsp;&nbsp;HTTP会话认证<br>&nbsp;&nbsp;HTTP Basic认证<br><br>1、Log4j的概念<br>&nbsp;&nbsp; Log4j中有三个主要的组件，它们分别是Logger、Appender和Layout，Log4j 允许开发人员定义多个Logger，每个Logger拥有自己的名字，Logger之间通过名字来表明隶属关系。有一个Logger称为Root，它永远 存在，且不能通过名字检索或引用，可以通过Logger.getRootLogger()方法获得，其它Logger通过 Logger.getLogger(String name)方法。<br>&nbsp;&nbsp; Appender则是用来指明将所有的log信息存放到什么地方，Log4j中支持多种appender，如 console、files、GUI components、NT Event Loggers等，一个Logger可以拥有多个Appender，也就是你既可以将Log信息输出到屏幕，同时存储到一个文件中。<br>&nbsp;&nbsp; Layout的作用是控制Log信息的输出方式，也就是格式化输出的信息。<br>&nbsp;&nbsp; Log4j中将要输出的Log信息定义了5种级别，依次为DEBUG、INFO、WARN、ERROR和FATAL，当输出时，只有级别高过配置中规定的 级别的信息才能真正的输出，这样就很方便的来配置不同情况下要输出的内容，而不需要更改代码，这点实在是方便啊。<br><br>2、Log4j的配置文件<br>&nbsp;&nbsp;虽然可以不用配置文件，而在程序中实现配置，但这种方法在如今的系统开发中显然是不可取的，能采用配置文件的地方一定一定要用配置文件。Log4j支持两 种格式的配置文件：XML格式和Java的property格式，本人更喜欢后者，首先看一个简单的例子吧，如下：<br><br>
<pre class=overflow> log4j.rootLogger=debug, stdout, R<br>&nbsp;&nbsp;log4j.appender.stdout=org.apache.log4j.ConsoleAppender<br>&nbsp;&nbsp;log4j.appender.stdout.layout=org.apache.log4j.PatternLayout<br><br>&nbsp;&nbsp;# Pattern to output the caller's file name and line number.<br>&nbsp;&nbsp;log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n<br><br>&nbsp;&nbsp;log4j.appender.R=org.apache.log4j.RollingFileAppender<br>&nbsp;&nbsp;log4j.appender.R.File=example.log<br>&nbsp;&nbsp;log4j.appender.R.MaxFileSize=100KB<br><br>&nbsp;&nbsp;# Keep one backup file<br>&nbsp;&nbsp;log4j.appender.R.MaxBackupIndex=1<br><br>&nbsp;&nbsp;log4j.appender.R.layout=org.apache.log4j.PatternLayout<br>&nbsp;&nbsp;log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n</pre>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br><br>&nbsp;&nbsp;首先，是设置root，格式为 log4j.rootLogger=[level],appenderName, ...，其中level就是设置需要输出信息的级别，后面是appender的输出的目的地，appenderName就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。配置日志信息输出目的地Appender，其语法为<br>&nbsp;&nbsp;log4j.appender.appenderName = fully.qualified.name.of.appender.class<br>&nbsp;&nbsp;log4j.appender.appenderName.option1 = value1<br>&nbsp;&nbsp;...<br>&nbsp;&nbsp;log4j.appender.appenderName.option = valueN<br>Log4j提供的appender有以下几种：<br>&nbsp;&nbsp;org.apache.log4j.ConsoleAppender（控制台）<br>&nbsp;&nbsp;org.apache.log4j.FileAppender（文件）<br>&nbsp;&nbsp;org.apache.log4j.DailyRollingFileAppender（每天产生一个日志文件）<br>&nbsp;&nbsp;org.apache.log4j.RollingFileAppender（文件大小到达指定尺寸的时候产生新文件）<br>&nbsp;&nbsp;org.apache.log4j.WriterAppender（将日志信息以流格式发送到任意指定的地方）<br>配置日志信息的格式（布局），其语法为：<br>&nbsp;&nbsp;log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class<br>&nbsp;&nbsp;log4j.appender.appenderName.layout.option1 = value1<br>&nbsp;&nbsp;....<br>&nbsp;&nbsp;log4j.appender.appenderName.layout.option = valueN<br>Log4j提供的layout有以下几种：<br>&nbsp;&nbsp;org.apache.log4j.HTMLLayout（以HTML表格形式布局），<br>&nbsp;&nbsp;org.apache.log4j.PatternLayout（可以灵活地指定布局模式），<br>&nbsp;&nbsp;org.apache.log4j.SimpleLayout（包含日志信息的级别和信息字符串），<br>&nbsp;&nbsp;org.apache.log4j.TTCCLayout（包含日志产生的时间、线程、类别等等信息） <br><br>3、Log4j在程序中的使用<br>&nbsp;&nbsp;要在自己的类中使用Log4j，首先声明一个静态变量Logger logger=Logger.getLog("classname")；在使用之前，用PropertyConfigurator.configure ("配置文件")配置一下，现在就可以使用了，用法如下：logger.debug("debug message")或者logger.info("info message")，看下面一个小例子：<br><br>
<pre class=overflow> import com.foo.Bar;<br>&nbsp;&nbsp;import org.apache.log4j.Logger;<br>&nbsp;&nbsp;import org.apache.log4j.PropertyConfigurator;<br>&nbsp;&nbsp;public class MyApp {<br>&nbsp;&nbsp;&nbsp;&nbsp;static Logger logger = Logger.getLogger(MyApp.class.getName());<br>&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// BasicConfigurator replaced with PropertyConfigurator.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PropertyConfigurator.configure(args[0]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info("Entering application.");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bar bar = new Bar();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;bar.doIt();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info("Exiting application.");<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;}</pre>
<br><br><br>[简介]<br><br>对于一个典型的Web应用，完善的认证和授权机制是必不可少的，在SpringFramework中，Juergen Hoeller提供的范例JPetStore给了一些这方面的介绍，但还远远不够，Acegi是一个专门为SpringFramework提供安全机制的 项目，全称为Acegi Security System for Spring，当前版本为0.5.1，就其目前提供的功能，应该可以满足绝大多数应用的需求。<br><br>本文的主要目的是希望能够说明如何在基于Spring构架的Web应用中使用Acegi，而不是详细介绍其中的每个接口、每个类。注意，即使对已经存在的Spring应用，通过下面介绍的步骤，也可以马上享受到Acegi提供的认证和授权。<br><br>[基础工作]<br>在你的Web应用的lib中添加Acegi下载包中的acegi-security.jar<br><br>[web.xml]<br>实现认证和授权的最常用的方法是通过filter，Acegi亦是如此，通常Acegi需要在web.xml添加以下5个filter:<br><br>
<pre class=overflow>&lt;filter&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi Channel Processing Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br>&nbsp;&nbsp;&lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-name&gt;targetClass&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-value&gt;net.sf.acegisecurity.securechannel.ChannelProcessingFilter&lt;/param-value&gt;<br>&nbsp;&nbsp;&lt;/init-param&gt;<br>&lt;/filter&gt;<br>&lt;filter&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi Authentication Processing Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br>&nbsp;&nbsp;&lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-name&gt;targetClass&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-value&gt;net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter&lt;/param-value&gt;<br>&nbsp;&nbsp;&lt;/init-param&gt;<br>&lt;/filter&gt;<br>&lt;filter&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi HTTP BASIC Authorization Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br>&nbsp;&nbsp;&lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-name&gt;targetClass&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-value&gt;net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter&lt;/param-value&gt;<br>&nbsp;&nbsp;&lt;/init-param&gt;<br>&lt;/filter&gt;<br>&lt;filter&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi Security System for Spring Auto Integration Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;filter-class&gt;net.sf.acegisecurity.ui.AutoIntegrationFilter&lt;/filter-class&gt;<br>&lt;/filter&gt;<br>&lt;filter&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi HTTP Request Security Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;filter-class&gt;net.sf.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;<br>&nbsp;&nbsp;&lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-name&gt;targetClass&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-value&gt;net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter&lt;/param-value&gt;<br>&nbsp;&nbsp;&lt;/init-param&gt;<br>&lt;/filter&gt;</pre>
<br><br>最先引起迷惑的是net.sf.acegisecurity.util.FilterToBeanProxy，Acegi自己的文档上解释是： &#8220;What&nbsp;&nbsp;FilterToBeanProxy does is delegate the Filter's methods through to a bean which is obtained from the <br>Spring application context. This enables the bean to benefit from the Spring application context lifecycle support and configuration flexibility.&#8221;，如希望深究的话，去看看源代码应该不难理解。<br><br>再下来就是添加filter-mapping了：<br>
<pre class=overflow>&lt;filter-mapping&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi Channel Processing Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi Authentication Processing Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi HTTP BASIC Authorization Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi Security System for Spring Auto Integration Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&nbsp;&nbsp;&lt;filter-name&gt;Acegi HTTP Request Security Filter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;</pre>
<br><br>这里，需要注意以下两点：<br>1) 这几个filter的顺序是不能更改的，顺序不对将无法正常工作；<br>2) 如果你的应用不需要安全传输，如https，则将"Acegi Channel Processing Filter"相关内容注释掉即可；<br>3) 如果你的应用不需要Spring提供的远程访问机制，如Hessian and Burlap，将"Acegi HTTP BASIC Authorization <br>Filter"相关内容注释掉即可。<br><br>[applicationContext.xml]<br>接下来就是要添加applicationContext.xml中的内容了，从刚才FilterToBeanFactory的解释可以看出，真正的filter都<br>在Spring的applicationContext中管理：<br><br>1) 首先，你的数据库中必须具有保存用户名和密码的table，Acegi要求table的schema必须如下：<br><br>
<pre class=overflow>CREATE TABLE users (<br>&nbsp;&nbsp;&nbsp;&nbsp;username VARCHAR(50) NOT NULL PRIMARY KEY,<br>&nbsp;&nbsp;&nbsp;&nbsp;password VARCHAR(50) NOT NULL,<br>&nbsp;&nbsp;&nbsp;&nbsp;enabled BIT NOT NULL<br>);<br>CREATE TABLE authorities (<br>&nbsp;&nbsp;&nbsp;&nbsp;username VARCHAR(50) NOT NULL,<br>&nbsp;&nbsp;&nbsp;&nbsp;authority VARCHAR(50) NOT NULL<br>);<br>CREATE UNIQUE INDEX ix_auth_username ON authorities ( username, authority );<br>ALTER TABLE authorities ADD CONSTRAINT fk_authorities_users foreign key (username) REFERENCES users<br>(username);</pre>
<br><br>2) 添加访问你的数据库的datasource和Acegi的jdbcDao，如下：<br><br>
<pre class=overflow>&lt;bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt;<br>&nbsp;&nbsp;&lt;property name="driverClassName"&gt;&lt;value&gt;${jdbc.driverClassName}&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="url"&gt;&lt;value&gt;${jdbc.url}&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="username"&gt;&lt;value&gt;${jdbc.username}&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="password"&gt;&lt;value&gt;${jdbc.password}&lt;/value&gt;&lt;/property&gt;<br>&lt;/bean&gt;<br>&lt;bean id="jdbcDaoImpl" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl"&gt;<br>&nbsp;&nbsp;&lt;property name="dataSource"&gt;&lt;ref bean="dataSource"/&gt;&lt;/property&gt;<br>&lt;/bean&gt;</pre>
<br><br>3) 添加DaoAuthenticationProvider:<br><br>
<pre class=overflow>&lt;bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider"&gt;<br>&nbsp;&nbsp;&lt;property name="authenticationDao"&gt;&lt;ref bean="authenticationDao"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="userCache"&gt;&lt;ref bean="userCache"/&gt;&lt;/property&gt;<br>&lt;/bean&gt;<br><br>&lt;bean id="userCache" class="net.sf.acegisecurity.providers.dao.cache.EhCacheBasedUserCache"&gt;<br>&nbsp;&nbsp;&lt;property name="minutesToIdle"&gt;&lt;value&gt;5&lt;/value&gt;&lt;/property&gt;<br>&lt;/bean&gt;</pre>
<br><br>如果你需要对密码加密，则在daoAuthenticationProvider中加入：&lt;property name="passwordEncoder"&gt;&lt;ref <br>bean="passwordEncoder"/&gt;&lt;/property&gt;，Acegi提供了几种加密方法，详细情况可看包<br>net.sf.acegisecurity.providers.encoding<br><br>4) 添加authenticationManager:<br><br>
<pre class=overflow>&lt;bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager"&gt;<br>&nbsp;&nbsp;&lt;property name="providers"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="daoAuthenticationProvider"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/list&gt;<br>&nbsp;&nbsp; &lt;/property&gt;<br>&lt;/bean&gt;</pre>
<br><br>5) 添加accessDecisionManager:<br><br>
<pre class=overflow>&lt;bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased"&gt;<br>&nbsp;&nbsp;&lt;property name="allowIfAllAbstainDecisions"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;false&lt;/value&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="decisionVoters"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt;&lt;ref bean="roleVoter"/&gt;&lt;/list&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&lt;/bean&gt;<br>&lt;bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/&gt;</pre>
<br><br>6) 添加authenticationProcessingFilterEntryPoint:<br><br>
<pre class=overflow>&lt;bean id="authenticationProcessingFilterEntryPoint" <br>class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"&gt;<br>&nbsp;&nbsp;&lt;property name="loginFormUrl"&gt;&lt;value&gt;/acegilogin.jsp&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="forceHttps"&gt;&lt;value&gt;false&lt;/value&gt;&lt;/property&gt;<br>&lt;/bean&gt;</pre>
<br><br>其中acegilogin.jsp是登陆页面，一个最简单的登录页面如下：<br><br>
<pre class=overflow>&lt;%@ taglib prefix='c' uri='http://java.sun.com/jstl/core' %&gt;<br>&lt;%@ page import="net.sf.acegisecurity.ui.AbstractProcessingFilter" %&gt;<br>&lt;%@ page import="net.sf.acegisecurity.AuthenticationException" %&gt;<br>&lt;html&gt;<br>&nbsp;&nbsp;&lt;head&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;Login&lt;/title&gt;<br>&nbsp;&nbsp;&lt;/head&gt;<br><br>&nbsp;&nbsp;&lt;body&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;h1&gt;Login&lt;/h1&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;form action="&lt;c:url value='j_acegi_security_check'/&gt;" method="POST"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;table&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tr&gt;&lt;td&gt;User:&lt;/td&gt;&lt;td&gt;&lt;input type='text' name='j_username'&gt;&lt;/td&gt;&lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tr&gt;&lt;td&gt;Password:&lt;/td&gt;&lt;td&gt;&lt;input type='password' name='j_password'&gt;&lt;/td&gt;&lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tr&gt;&lt;td colspan='2'&gt;&lt;input name="submit" type="submit"&gt;&lt;/td&gt;&lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;tr&gt;&lt;td colspan='2'&gt;&lt;input name="reset" type="reset"&gt;&lt;/td&gt;&lt;/tr&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/table&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/form&gt;<br>&nbsp;&nbsp;&lt;/body&gt;<br>&lt;/html&gt;</pre>
<br><br>7) 添加filterInvocationInterceptor:<br><br>
<pre class=overflow>&lt;bean id="filterInvocationInterceptor" <br>class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor"&gt;<br>&nbsp;&nbsp;&lt;property name="authenticationManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="authenticationManager"/&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="accessDecisionManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="accessDecisionManager"/&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="objectDefinitionSource"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\A/sec/administrator.*\Z=ROLE_SUPERVISOR<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\A/sec/user.*\Z=ROLE_TELLER<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/value&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&lt;/bean&gt;</pre>
<br><br>这里请注意，要objectDefinitionSource中定义哪些页面需要权限访问，需要根据自己的应用需求进行修改，我上面给出<br>的定义的意思是这样的：<br>a. CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON意思是在比较请求路径时全部转换为小写<br>b. \A/sec/administrator.*\Z=ROLE_SUPERVISOR意思是只有权限为ROLE_SUPERVISOR才能访问/sec/administrator*的页面<br>c. \A/sec/user.*\Z=ROLE_TELLER意思是只有权限为ROLE_TELLER的用户才能访问/sec/user*的页面<br><br>8) 添加securityEnforcementFilter:<br><br>
<pre class=overflow>&lt;bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter"&gt;<br>&nbsp;&nbsp;&lt;property name="filterSecurityInterceptor"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="filterInvocationInterceptor"/&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="authenticationEntryPoint"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="authenticationProcessingFilterEntryPoint"/&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&lt;/bean&gt;</pre>
<br><br>9) 添加authenticationProcessingFilter:<br><br>
<pre class=overflow>&lt;bean id="authenticationProcessingFilter" <br>class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter"&gt;<br>&nbsp;&nbsp;&lt;property name="authenticationManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="authenticationManager"/&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="authenticationFailureUrl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;/loginerror.jsp&lt;/value&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="defaultTargetUrl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;/&lt;/value&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="filterProcessesUrl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;/j_acegi_security_check&lt;/value&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&lt;/bean&gt;</pre>
<br>其中authenticationFailureUrl是认证失败的页面。<br><br>10) 如果需要一些页面通过安全通道的话，添加下面的配置:<br><br>
<pre class=overflow>&lt;bean id="channelProcessingFilter" class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter"&gt;<br>&nbsp;&nbsp;&lt;property name="channelDecisionManager"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="channelDecisionManager"/&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;property name="filterInvocationDefinitionSource"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\A/sec/administrator.*\Z=REQUIRES_SECURE_CHANNEL<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\A.*\Z=REQUIRES_INSECURE_CHANNEL<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/value&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&lt;/bean&gt;<br><br>&lt;bean id="channelDecisionManager" class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl"&gt;<br>&nbsp;&nbsp;&lt;property name="channelProcessors"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="secureChannelProcessor"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="insecureChannelProcessor"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/list&gt;<br>&nbsp;&nbsp;&lt;/property&gt;<br>&lt;/bean&gt;<br>&lt;bean id="secureChannelProcessor" class="net.sf.acegisecurity.securechannel.SecureChannelProcessor"/&gt;<br>&lt;bean id="insecureChannelProcessor" class="net.sf.acegisecurity.securechannel.InsecureChannelProcessor"/&gt;</pre>
<br><br>[缺少了什么？]<br>Acegi目前提供了两种"secure object"，分别对页面和方法进行安全认证管理，我这里介绍的只是利用<br>FilterSecurityInterceptor对访问页面的权限控制，除此之外，Acegi还提供了另外一个Interceptor――<br>MethodSecurityInterceptor，它结合runAsManager可实现对对象中的方法的权限控制，使用方法可参看Acegi自带的文档<br>和contact范例。<br><br>[最后要说的]<br>本来以为只是说明如何使用Acegi而已，应该非常简单，但真正写起来才发现想要条理清楚的理顺所有需要的bean还是很<br>困难的，但愿我没有遗漏太多东西，如果我的文章有什么遗漏或错误的话，还请参看Acegi自带的quick-start范例，但请<br>注意，这个范例是不能直接拿来用的。<br>分析和学习Spring中的jpetstore用户管理 <br>&nbsp;&nbsp;存在用户的系统，必然需要用户的登录和认证，今天就通过分析Spring中自带的jpetstore的例子来学习一下如何实现在Spring构架的系统中用户登录。<br>1、首先从注册用户开始，先看看jpetstore-servlet.xml中关于注册用户的bean定义，从定义命名中就可以看出下面这段就是注册用户的：<br>&nbsp;&nbsp;
<pre class=overflow>&lt;bean name="/shop/newAccount.do" class="org.springframework.samples.jpetstore.web.spring.AccountFormController"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="petStore"&gt;&lt;ref bean="petStore"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="validator"&gt;&lt;ref bean="accountValidator"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="successView"&gt;&lt;value&gt;index&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;/bean&gt;</pre>
<br>1). formView呢？从AccountFormController的构造函数中得到，原来为EditAccountForm；&nbsp;&nbsp;<br>2). EditoAccountForm.jsp中显得非常乱，其实没有多少难理解的地方，最主要的是这个form既是添加新用户的，又是编辑用户信息的，所以显得有点乱糟糟的。<br>2、添加好了新用户，接下来看看如何登录，在jpetstore-servlet中发现这两个相关bean定义，如下：<br>&nbsp;&nbsp;
<pre class=overflow>&lt;bean name="/shop/signon.do" class="org.springframework.samples.jpetstore.web.spring.SignonController"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="petStore"&gt;&lt;ref bean="petStore"/&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;/bean&gt;<br>&nbsp;&nbsp;&lt;bean name="/shop/signonForm.do" class="org.springframework.web.servlet.mvc.ParameterizableViewController"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="viewName"&gt;&lt;value&gt;SignonForm&lt;/value&gt;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;/bean&gt;</pre>
<br>1). 第二个bean是在运行时用户输入用户名和密码的form，叫做SignonForm，对于这个 ParameterizableViewController，用文档里的话说这是最简单的Controller，其作用就是在运行中指向 Controller而不是直接指向jsp文件，仅此而已。<br>2). SignonForm.jsp，里面就是一个简单的form，其action就是第一个bean，即/shop/signon.do，最需要注意的是 signonForwardAction，其主要作用是forward到需要输入用户名和密码的那个页面上去，这个变量哪里来的呢？看看下面：<br>&nbsp;&nbsp;
<pre class=overflow>&lt;bean id="secureHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="interceptors"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;ref bean="signonInterceptor"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/list&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="urlMap"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;map&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;entry key="/shop/editAccount.do"&gt;&lt;ref local="secure_editAccount"/&gt;&lt;/entry&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;entry key="/shop/listOrders.do"&gt;&lt;ref local="secure_listOrders"/&gt;&lt;/entry&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;entry key="/shop/newOrder.do"&gt;&lt;ref local="secure_newOrder"/&gt;&lt;/entry&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;entry key="/shop/viewOrder.do"&gt;&lt;ref local="secure_viewOrder"/&gt;&lt;/entry&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/map&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/property&gt;<br>&nbsp;&nbsp;&lt;/bean&gt;</pre>
<p><br>&nbsp;&nbsp;原来，上面的signonInterceptor实现了preHandle，因此在请求上面的map页面时，首先要经过这个Interceptor，看看 SignonInterceptor的源码，原来在其中为signon.jsp赋予一个signonForwardAction对象，呵呵，总算明白了。<br>3). 接下来去学习一下SignonController，其主体部分中可以看出，首先取出用户输入的username和password，然后到数据库中验证 有没有这个用户，如果没有这个用户，返回各错误页面；如果成功，首先生成一个UserSession对象，在request的session加入这个 userSession，注意这部分代码中给出了PagedListHolder分页的简单使用方法，关于分页显示，以后再学习吧。<br>3、登录成功后，就可以根据不同的用户设施不同的行为了，取得用户信息，无非就是从session取出userSession即可。</p>
<p>链接：<a href="http://www.matrix.org.cn/resource/article/1/1730_Acegi.html"><u><font color=#0000ff>http://www.matrix.org.cn/resource/article/1/1730_Acegi.html</font></u></a></p>
<img src ="http://www.blogjava.net/junky/aggbug/126773.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-06-28 12:44 <a href="http://www.blogjava.net/junky/archive/2007/06/28/126773.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi 参考手册(V1.0.4) 翻译</title><link>http://www.blogjava.net/junky/archive/2007/06/28/126772.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Thu, 28 Jun 2007 04:42:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/06/28/126772.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/126772.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/06/28/126772.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/126772.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/126772.html</trackback:ping><description><![CDATA[<div class=post-body>
<h1><a name=AcegiReferencePreface-%E5%BA%8F%E8%A8%80></a><strong>序言</strong></h1>
<p>&nbsp;&nbsp;&nbsp; 译者：<span class=nobr><a title="Visit page outside Confluence" href="http://leondu.javaeye.com/" rel=nofollow><u><font color=#0000ff>leondu<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>&nbsp;，作者保留版权，转载请注明出处。</p>
<p>&nbsp;&nbsp;&nbsp; Acegi Security为基于J2EE的企业应用软件提供全面的安全解决方案。正如你在本手册中看到的那样，我们尝试为您提供有用的，高可配置的安全系统。</p>
<p>&nbsp;&nbsp;&nbsp; 安全是一个永无止境的目标，获取一个全面的，系统级的实现方式是至关重要的。在安全界，我们鼓励你采用"分层安全"，这样每个层都确保自身尽可能的安全，另 外的层提供另外的安全。每个层自身越"紧密"，你的系统就越鲁棒和安全。在底层，你要处理传输入安全和系统认证，减少"中间人攻击"（man-in-the-middle attacks）。接下来你要使用防火墙，结合VPN或者IP安全来确保只有认证过的系统能够尝试连接。在企业环境中，你可能部署DMZ（demilitarized zone，隔离区）来把面向公众的服务器和后端的数据和应用服务器分隔开。在以非授权用户运行进程和文件系统安全最大化上，你的操作系统也将扮演一个关键的角色。接下来你要防止针对系统的拒绝服务和暴力攻击。入侵检测系统在检测和应对攻击方面尤其有用，这些系统可以实时屏蔽恶意TCP/IP地址。在更高层上，你的Java虚拟机需要进行配置，将授予不同Java类型的权限最小化，然后，你的应用程序要对添加针对自身特定问题域的安全配置。Acegi Security使后者－应用程序安全变得容易。</p>
<p>&nbsp;&nbsp;&nbsp; 当然，你要正确对待上述提到的每个安全层，以及包含于每个层的管理因素。这样的管理因素具体包括：安全公告监测，补丁，人工诊断，审计，变更管理，工程管理系统，数据备份，灾难恢复，性能评测，负载监测，集中日志，应急反应程序等等。</p>
<p>&nbsp;&nbsp;&nbsp; Acegi Security专注于在企业应用安全层为您提供帮助，你将会发现和各式各样的需求和商业问题领域一样多。银行系统的需求和电子商务应用的需求不同。电子商务应用和售卖军用自动工具的公司的需求不同。这些客户化的需求使得应用安全显得有趣，富有挑战性而且物有所值。</p>
<p>&nbsp;&nbsp;&nbsp; 本手册为Acegi Security 1.0.0的发布而大规模重新组织。请先阅读Part I 架构概览。手册的其余部分按照传统的手册方式编排，需要一定的基础才能阅读。</p>
<p>&nbsp;&nbsp;&nbsp; 我们希望您会觉得手册有用，并且欢迎您提供反馈意见和建议。</p>
<p>&nbsp;&nbsp;&nbsp; 最后，欢迎加入Acegi Security社区。</p>
<h1><a name=Chapter1.Introduction-PartI.%E6%9E%B6%E6%9E%84%E6%A6%82%E8%A7%88></a>Part I. <strong>架构概览</strong></h1>
<p>&nbsp;&nbsp;&nbsp; 象其他的软件一样，Acegi Security也有在整个框架中都会使用的特定核心接口，类，和概念抽象。在手册的这一部分，在检视这些规划和执行Acegi Security集成所必须的核心要素之前，我们先介绍Acegi Security。</p>
<h1><a name=Chapter1.Introduction-%E7%AC%AC%E4%B8%80%E7%AB%A0%7B%7D.%E7%AE%80%E4%BB%8B></a><strong>第一章</strong><strong>.</strong> <strong>简介</strong></h1>
<h2><a name=Chapter1.Introduction-1.1.AcegiSecurity%7B%7D%7B%7D%E6%98%AF%E4%BB%80%E4%B9%88%7B%7D%3F></a><strong>1.1. Acegi Security</strong><strong>是什么</strong><strong>?</strong></h2>
<p>&nbsp;&nbsp;&nbsp; Acegi Security为基于J2EE的企业软件应用提供全面的安全服务。特别是使用领先的J2EE解决方案－Srping框架开发的项目。如果您不是使用Spring开发企业应用，我们温馨提醒您仔细研究一下。熟悉Spring，尤其是依赖注射原理，会极大的帮助你快速掌握Acegi Security。</p>
<p>&nbsp;&nbsp;&nbsp; 人们使用Acegi Security有很多种原因，不过通常吸引他们到这个项目的原因是他们在J2EE的 Servlet Specification 或者 EJB Specification中找不到迫切需要的典型企业应用场景。提到这些规范，特别要提出的是他们不是在WAR或者EAR级别可移植的。这样，如果你切换服务器环境，一般来说你要在目标环境中花费很多工夫来重新配置你的应用安全。使用Acegi Security解决了这些问题，并且为你提供了很多其他有用的，完全可定制的安全特性。</p>
<p>&nbsp;&nbsp;&nbsp; 如你所知，安全包含两个主要操作。第一个被称为"认证"，是为用户建立一个它所声明的principal。Principal通常代表用户，设备，或者其他能在你的应用中执行操作的其他系统。"授权"指判定一个principal能否在你的系统中执行某个操作。在到达授权判断之前，principal的的身份认证已经由认证过程执行过了。这些概念是通用的，不是Acegi Security特有的。</p>
<p>&nbsp;&nbsp;&nbsp; 在认证层面，Acegi Security广泛支持各种认证模块。这些认证模块绝大多数是第三方提供，或者相关的标准组织开发的，例如Internet Engineering Task Force。作为补充，Acegi Security自己也提供了一些认证功能。&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; Acegi Security当前支持如下的认证技术。</p>
<p>&#8226; HTTP BASIC authentication headers (an IEFT RFC-based standard)</p>
<p>&#8226; HTTP Digest authentication headers (an IEFT RFC-based standard)</p>
<p>&#8226; HTTP X.509 client certificate exchange (an IEFT RFC-based standard)</p>
<p>&#8226; LDAP (a very common approach to cross-platform authentication needs, especially in large environments)</p>
<p>&#8226; Form-based authentication (for simple user interface needs)</p>
<p>&#8226; Computer Associates Siteminder</p>
<p>&#8226; JA-SIG Central Authentication Service (otherwise known as CAS, which is a popular open source single sign on system)</p>
<p>&#8226; Transparent authentication context propagation for Remote Method Invocation (RMI) and HttpInvoker (a Spring remoting protocol)</p>
<p>&#8226; Automatic "remember-me" authentication (so you can tick a box to avoid re-authentication for a predetermined period of time)</p>
<p>&#8226; Anonymous authentication (allowing every call to automatically assume a particular security identity)</p>
<p>&#8226; Run-as authentication (which is useful if one call should proceed with a different security identity)</p>
<p>&#8226; Java Authentication and Authorization Service (JAAS)</p>
<p>&#8226; Container integration with JBoss, Jetty, Resin and Tomcat (so you can still use Container Manager Authentication if desired)</p>
<p>&#8226; 你自己的认证系统 (如下所示)</p>
<p>&nbsp;&nbsp;&nbsp; 很多独立软件供应商(ISVs)选择Acegi Security是因为它具有丰富的认证模块。这样无论他们的终端客户需要什么，他们都可以快速集成到他们的系统中，不用花很多工夫或者让终端客户改变环境。如果Acegi Security System for Spring的7个认证模块还没有满足你的需求的话，Acegi Security是一个开放的系统，很容易写你自己的认证机制。许多Acegi Security的企业用户需要和"遗留"系统集成，这些遗留系统不遵循任何安全标准，Acegi Security能够和这样的系统"合作愉快"。</p>
<p>&nbsp;&nbsp;&nbsp; 有时候基本的认证是不够的。有时候你需要根据principal和应用交互的方式来应用不同的安全措施。例如，你可能为了防止密码被窃取，或者防止终端用户受到"中间人"攻击，需要保证到达的是请求通过HTTPS的。或者，你要确保是一个真正的人而不是某种机器人或者自动进程在发送请求。这对于保护密码恢复不受暴力破解攻击，或者防止他人很容易的复制你应用的关键内容。为了帮助你实现这些目标，Acegi Security完全支持自动"通道安全"("channel security")，以及集成Jcaptcha来检测是否是真正人类用户。</p>
<p>&nbsp;&nbsp;&nbsp; Acegi Security不仅提供了认证功能，而且提供了完备的授权功能。在授权方面主要有三个领域，授权web请求，授权方法调用，授权存取单个领域对象实例。为了帮助你理解这些区别，对照考虑一下Servlet 规范中的web模式安全的授权功能，EJB容器管理安全以及文件系统安全。Acegi Security提供了所有这些重要领域的完备功能，我们将在本手册的后面介绍。</p>
<h2><a name=Chapter1.Introduction-1.2.%E5%8E%86%E5%8F%B2></a><strong>1.2.</strong> <strong>历史</strong></h2>
<p>&nbsp;&nbsp;&nbsp; Acegi Security始于2003年晚期，当时在Spring Developers邮件列表中有人提问是否有人考虑提供一个基于Spring的安全实现。当时，Srping的社区是相对比较小的（尤其是和今天相比！），实际上Spring本身也是2003年早期才作为一个SourceForge项目出现的。对此问题的回应是它确实是一个值得研究的领域，虽然限于时间无法进行深入。</p>
<p>&nbsp;&nbsp;&nbsp; 有鉴于此，这个简单的安全实现虽然构建了但是并没有发布。几周以后，Spring社区的其他成员询问了安全框架，代码就被提供给了他们。</p>
<p>随后又有人请求，到了2004年一月，大约有20人左右在使用这些代码。另外一些人加入到这些先行的用户中来，并建议建立一个SourceForge项目，这个项目在2004年3月建立起来。</p>
<p>&nbsp;&nbsp;&nbsp; 在早期，该项目自身并不具备任何认证模块。认证过程依赖容器管理安全（Container Managed Security）而Acegi Security注重授权。在一开始这样是合适的，但是随着越来越多的用户要求额外的容器支持，基于容器的认证的限制就显示出来了。另外一个相关的问题是添加新的JAR文件到容器的classpath，通常会让最终用户感到困惑并且配置错误。</p>
<p>&nbsp;&nbsp;&nbsp; 随后，Acegi Security加入了认证服务。大约一年后，Acegi Security成为了一个Spring Framework官方子项目。在2年半多的在多个软件项目中的活跃使用以及数以百计的改进和社区贡献，1.0.0最终版在2006年5月发布。</p>
<p>&nbsp;&nbsp; 今天，Acegi Security成为一个强大而活跃的社区。在支持论坛上有数以千计的帖子。14个开发人员专职开发，一个活跃的社区也定期共享补丁并支持他们的同侪。</p>
<h2><a name=Chapter1.Introduction-1.3.%E5%8F%91%E8%A1%8C%E7%89%88%E6%9C%AC%E5%8F%B7></a><strong>1.3.</strong> <strong>发行版本号</strong></h2>
<p>&nbsp;&nbsp;&nbsp; 理解Acegi Security的版本号是非常好处的，它可以帮助你判定升级的到新的版本是否需要花费很大精力。我们的正式发行版本使用Apache Portable Runtime Project版本指引，可以在下述网站查看<span class=nobr><a title="Visit page outside Confluence" href="http://apr.apache.org/versioning.html" rel=nofollow><u><font color=#0000ff>http://apr.apache.org/versioning.html<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>。为了您查看方便，我们引用该页的说明部分如下：</p>
<p>&nbsp;&nbsp;&nbsp; "版本号由三个部分的整数组成：主版本号（MAJOR）、副版本号（MINOR）、补丁版本号（PATCH）。主要的含义是主版本号（MAJOR）是不兼容的，API大规模升级。副版本号（MINOR）在源文件和可执行版和老版本保持兼容，补丁版本号（PATCH）则意味着向前和向后的完全兼容"。</p>
<h1><a name=Chapter2.TechnicalOverview-%E7%AC%AC%E4%BA%8C%E7%AB%A0%7B%7D.%E6%8A%80%E6%9C%AF%E6%A6%82%E8%A7%88></a><strong>第二章</strong><strong>.</strong> <strong>技术概览</strong></h1>
<h2><a name=Chapter2.TechnicalOverview-2.1.%E8%BF%90%E8%A1%8C%E6%97%B6%E7%8E%AF%E5%A2%83></a><strong>2.1.</strong> <strong>运行时环境</strong></h2>
<p>&nbsp;&nbsp;&nbsp; Acegi Security可以在JRE1.3中运行。这个发行版本中支持也Java 5.0，尽管对应的Java类型被分开打包到一个后缀是"tiger"的包中。因为Acegi Security致力于以一种自包含的方式运行，因此不需要在JRE中放置任何特殊的配置文件。特别无需配置Java Authentication and Authorization Service (JAAS)策略文件或者将Acegi Security放置到通用的classpath路径中。</p>
<p>&nbsp;&nbsp;&nbsp; 同样的，如果你使用EJB容器或者Servlet容器，同样无需放置任何特别的配置文件或者将Acegi Security包含在服务器的类加载器（classloader）中。</p>
<p>&nbsp;&nbsp;&nbsp; 上述的设计提供了最大的部署灵活性，你可以直接把目标工件（JAR, WAR 或者 EAR)）直接从一个系统copy到另一个系统，它马上就可以运行起来。</p>
<h2><a name=Chapter2.TechnicalOverview-2.2.%E5%85%B1%E4%BA%AB%E7%BB%84%E4%BB%B6></a><strong>2.2.</strong> <strong>共享组件</strong></h2>
<p>&nbsp;&nbsp;&nbsp; 让我们来看看Acegi Security中最重要的一些共享组件。所谓共享组件是指在框架中处于核心地位，系统脱离了它们之后就不能运行。这些Java类型代表了系统中其他部分的构建单元，因此理解它们是非常重要的，即使你不需要直接和它们打交道。</p>
<p>&nbsp;&nbsp;&nbsp; 最基础的对象是SecurityContextHolder。在这里存储了当前应用的安全上下文（security context），包括正在使用应用程序的principal的详细信息。SecurityContextHolder默认使用ThreadLocal来存储这些详细信息，这意味着即便安全上下文（security context）没有被作为一个参数显式传入，它仍然是可用的。如果在当前principal的请求处理后清理线程,那么用这种方式使用ThreadLocal是非常安全的。当然， Acegi Security自动为你处理这些，所以你无需担心。</p>
<p>&nbsp;&nbsp;&nbsp; 有些应用程序由于使用线程的方式而并不是完全适用ThreadLocal。例如，Swing客户端可能需要一个Java Virtual Machine中的所有线程都使用同样的安全上下文（security context）。在这种情况下你要使用SecurityContextHolder.MODE_GLOBAL模式。另外一些应用程序可能需要安全线程产生的线程拥有同样的安全标识符（security identity）。这可以通过SecurityContextHolder.MODE_INHERITABLETHREADLOCAL来实现。你可以通过两种方法来修改默认的SecurityContextHolder.MODE_THREADLOCAL。第一种是设置一个系统属性。或者，调用SecurityContextHolder的一个静态方法。大部分的应用程序不需要修改默认值，不过如果你需要，那么请查看SecurityContextHolder的JavaDocs获取更多信息。</p>
<p>&nbsp;&nbsp;&nbsp; 我们在SecurityContextHolder中存储当前和应用程序交互的principal的详细信息。Acegi Security使用一个Authentication对象来代表这个信息。尽管你通常不需要自行创建一个Authentication对象，用户还是经常会查询Authentication对象。</p>
<p>你可以在你的应用程序中的任何地方使用下述的代码块：</p>
<div class=code>
<div class=codeContent>
<pre class=code-java><span class=code-object>Object</span> obj =SecurityContextHolder.getContext().getAuthentication().getPrincipal();<span class=code-keyword>if</span> (obj <span class=code-keyword>instanceof</span> UserDetails) {<span class=code-object>String</span> username =((UserDetails)obj).getUsername();} <span class=code-keyword>else</span> {<span class=code-object>String</span> username =obj.toString();}</pre>
</div>
</div>
<p>&nbsp;&nbsp;&nbsp; 上述的代码展示了一些有趣的联系和关键的对象。首先，你会注意到在SecurityContextHolder和Authentication之间有一个媒介对象。SecurityContextHolder.getContext() 方法实际上返回一个SecurityContext。Acegi Security使用若干个不同的SecurityContext实现，以备我们需要存储一些和principal无关的特殊信息。一个很好的例子就是我们的Jcaptcha集成，它需要知道一个需求是否是由人发起的。这样的判断和principal是否通过认证完全没有关系，因此我们将它保存在SecurityContext中。</p>
<p>&nbsp;&nbsp;&nbsp; 从上述的代码片段可以看出的另一个问题是你可以从一个Authentication对象中获取一个principal。Principal只是一个对象。通常可以把它cast为一个UserDetails对象。UserDetails在Acegi Security中是一个核心接口，它以一种扩展以及应用相关的方式来展现一个principal。可以把UserDetails看作是你的用户数据库和Acegi Security在SecurityContextHolder所需要的东西之间的一个适配器（adapter）。作为你自己用户数据库的一个展现，你可能经常要把它cast到你应用程序提供的原始对象，这样你就可以调用业务相关的方法(例如 getEmail(), getEmployeeNumber())。</p>
<p>&nbsp;&nbsp;&nbsp; 现在你可能已经开始疑惑，那我什么时候提供UserDetails对象呢？我要如何提供呢？</p>
<p>&nbsp;&nbsp;&nbsp; 我想你告诉过我这个东西是声明式的，我不需要写任何Java代码－那怎么做到呢？对此的简短回答就是有一个叫做UserDetailsService的特殊接口。这个接口只有一个方法，接受一个Sring类型的参数并返回一个UserDetails。Acegi Security提供的大多数认证提供器将部分认证过程委派给UserDetailsService。UserDetailsService用来构建保存在SecurityContextHolder中的Authentication对象。好消息是我们提供若干个UserDetailsService的实现，包括一个使用in-memory map和另一个使用JDBC的。</p>
<p>&nbsp;&nbsp;&nbsp; 大多数用户还是倾向于写自己的实现，这样的实现经常就是简单的构建于已有的Data Access Object (DAO)上，这些DAO展现了他们的雇员、客户、或者其他企业应用程序中的用户。要记得这样做的好处，不论你的UserDetailsService返回什么，它总是可以从SecurityContextHolder中获取，象上面的代码显示的那样。</p>
<p>&nbsp;&nbsp;&nbsp; 除了principal，Authentication提供的另一个重要方法就是getAuthorities（）。这个方法返回一个GrantedAuthority对象数组。GrantedAuthority，毫无疑问，就是授予principal的权限。这些权限通常是"角色"，例如ROLE_ADMINISTRATOR 或者ROLE_HR_SUPERVISOR。这些角色稍后配置到web授权，方法授权和领域对象授权。Acegi Security的其他部分能够处理这些权限，并且期待他们被提供。通常你会从UserDetailsService中返回GrantedAuthority对象。</p>
<p>&nbsp;&nbsp;&nbsp; 通常GrantedAuthority对象都是应用范围的权限。它们都不对应特定的领域对象。因此，你应该不会有一个代表54号员工对象的GrantedAuthority，因为这样会有数以千计的authority，你马上就会用光所有内存（或者，至少会让系统花太长时间来认证一个用户）。当然，Acegi Security会高效的处理这种普遍的需求，但是你不会使用领域对象安全功能来实现这个目的。</p>
<p>&nbsp;&nbsp;&nbsp; 最后，但不是不重要，你有时候需要在HTTP 请求之间存储SecurityContext。另外有些时候你在每次请求的时候都会重新认证principal，不过大部分时候你会存储SecurityContext。HttpSessionContextIntegrationFilter在HTTP之间存储SecurityContext。正如类名字显示的那样，它使用HttpSession来进行存储。基于安全原因，你永远都不要直接和HttpSession交互。没有理由这么做，所以记得使用SecurityContextHolder来代替。</p>
<p>让我们回忆一下，Acegi Security的基本组成构件是：</p>
<p>&#8226; SecurityContextHolder,提供对SecurityContext的所有访问方式。</p>
<p>&#8226; SecurityContext, 存储Authentication以及可能的请求相关的安全信息。</p>
<p>&#8226; HttpSessionContextIntegrationFilter, 在web请求之间把SecurityContext存储在HttpSession中。</p>
<p>&#8226; Authentication, 以Acegi Security的方式表现principal。</p>
<p>&#8226; GrantedAuthority, 表示赋予一个principal的应用范围的权限。</p>
<p>&#8226; UserDetails, 为从你的应用程序DAO中获取必要的信息来构建一个Authentication 对象。</p>
<p>&#8226; UserDetailsService,用传入的String类型的username（或者认证ID，或类似）来创建一个UserDetails。</p>
<p>&nbsp;&nbsp;&nbsp; 现在你已经理解了这些重复使用的组件，让我们仔细看看认证过程吧。</p>
<h2><a name=Chapter2.TechnicalOverview-2.3.%E8%AE%A4%E8%AF%81></a><strong>2.3.</strong> <strong>认证</strong></h2>
<p>&nbsp;&nbsp;&nbsp; 正如我们在手册开始部分所说的那样，Acegi Security适用于很多认证环境。虽然我们建议大家使用Acegi Security自身的认证功能而不是和容器管理认证（Container Managed Authentication）集成，但是我们仍然支持这种和你私有的认证系统集成的认证。让我们先从Acegi Security完全自行管理管理web安全的角度来探究一下认证，这也是最复杂和最通用的情形。</p>
<p>&nbsp;&nbsp;&nbsp; 想象一个典型的web应用的认证过程：</p>
<p>1．你访问首页，点击一个链接。</p>
<p>2．一个请求被发送到服务器，服务器判断你是否请求一个被保护的资源。</p>
<p>3．因为你当前未被认证，服务器发回一个回应，表明你必须通过认证。这个回应可能是一个HTTP回应代码，或者重定向到一个特定的网页。</p>
<p>4．基于不同的认证机制，你的浏览器会重定向到一个网页好让你填写表单，或者浏览器会用某种方式获取你的身份认证（例如一个BASIC认证对话框，一个cookie，一个X509证书等等。）。</p>
<p>5．浏览器会发回给服务器一个回应。可能是一个包含了你填写的表单内容的HTTP POST，或者一个包含你认证详细信息的HTTP header。</p>
<p>6．接下来服务器会判断提供的认证信息是否有效。如果它们有效，你进入到下一步。如果它们无效，那么通常请求你的浏览器重试一次（你会回到上两步）。</p>
<p>7．你引发认证的那个请求会被重试。但愿你认证后有足够的权限访问那些被保护的资源。如果你有足够的访问权限，请求就会成功。否则，你将会受到一个意味"禁止"的HTTP403错误代码。</p>
<p>&nbsp;&nbsp;&nbsp; 在Acegi Security中，对应上述的步骤，有对应的类。主要的参与者（按照被使用的顺序）是：ExceptionTranslationFilter， AuthenticationEntryPoint， 认证机制(authentication mechanism)， 以及AuthenticationProvider。</p>
<p>&nbsp;&nbsp;&nbsp; ExceptionTranslationFilter是Acegi Security用来检测任何抛出的安全异常的过滤器(filter)。这种异常通常是由AbstractSecurityInterceptor抛出的，它是授权服务的主要提供者。我们将会在下一部分讨论AbstractSecurityInterceptor，现在我们只需要知道它产生Java异常，并且对于HTTP或者如何认证一个principal一无所知。反而是ExceptionTranslationFilter提供这样的服务，它负责要么返回403错误代码(如果principal通过了认证，只是缺少足够的权限，象上述第7步那样)，要么加载一个AuthenticationEntryPoint (如果principal还没有被认证，那么我们要从第3步开始)。</p>
<p>&nbsp;&nbsp;&nbsp; AuthenticationEntryPoint负责上述的第3步。如你所想，每个web应用都有一个默认的认证策略（象Acegi Security中几乎所有的东西一样，它也是可配置的，不过我们现在还是还是从简单开始）。每个主流的认证系统都有它自己的AuthenticationEntryPoint实现，负责执行第3步中描述的动作。</p>
<p>&nbsp;&nbsp;&nbsp; 当浏览器确定要发送你的认证信息（HTTP 表单或者HTTP header），服务器上需要有什么东西来"收集"这些认证信息。现在我们在上述的第6步。在Acegi Security中对从用户代理（通常是浏览器）收集认证信息有一个特定的名字，这个名字是"认证机制（authentication mechanism）"。当认证信息从客户代理收集过来以后，一个"认证请求（Authenticationrequest）"对象被创建，并发送到AuthenticationProvider。</p>
<p>&nbsp;&nbsp;&nbsp; Acegi Security中认证的最后一环是一个AuthenticationProvider。非常简单，它的职责是取用一个认证请求（Authentication request）对象，并且判断它是否有效。这个provider要么抛出一个异常，要么返回一个组装完毕的Authentication对象。还记得我们的好朋友UserDetails 和 UserDetailsService吧？如果没有，回到前一部分重新回忆一下。大部分的AuthenticationProviders都会要求UserDetailsService提供一个UserDetails对象。如前所述，大部分的应用程序会提供自己的UserDetailsService，尽管有些会使用Acegi Security提供的JDBC或者 in-memory实现。作为成品的UserDetails 对象，特别是其中的GrantedAuthority[]s，在构建完备的Authentication对象时会被使用。</p>
<p>&nbsp;&nbsp;&nbsp; 当认证机制（authentication mechanism）取回组装完全的Authentication对象后，它将会相信请求是有效的，将Authentication放到SecurityContextHolder中，并且将原始请求取回（上述第7步）。反之，AuthenticationProvider则拒绝请求，认证机制（authentication mechanism）会请求用户重试（上述第2步）。</p>
<p>&nbsp;&nbsp;&nbsp; 在讲述典型的认证流程的同时，有个好消息是Acegi Security不关心你是如何把Authentication放到SecurityContextHolder内的。唯一关键的是在AbstractSecurityInterceptor授权一个请求之前，在SecurityContextHolder中包含一个代表了principal的Authentication。</p>
<p>&nbsp;&nbsp;&nbsp; 你可以（很多用户确实）实现自己的过滤器（filter）或者MVC控制器（controller）来提供和不是基于Acegi Security的认证系统交互。例如，你可能使用使用容器管理认证（Container Managed Authentication），从ThreadLocal或者JNDI中获取当前用户信息，使得它有效。或者，你工作的公司有一个遗留的私有认证系统，而它是公司"标准"，你对它无能为力。在这种情况之下也是非常容易让Acegi Security运作起来，提供认证能力。你所需要做的是写一个过滤器（或等价物）从某处读取第三方用户信息，构建一个Acegi Security式的Authentication对象，把它放到SecurityContextHolder中。这非常容易做，也是一种广泛支持的集成方式。</p>
<h2><a name=Chapter2.TechnicalOverview-2.4.%E5%AE%89%E5%85%A8%E5%AF%B9%E8%B1%A1></a><strong>2.4.</strong> <strong>安全对象</strong></h2>
<p>&nbsp;&nbsp;&nbsp; 如果你熟悉AOP，你会知道有很多种advice可用：before, after, throws 和 around。around advice非常有用，因为它能够选择是否选择是否执行一个方法调用，是否修改返回值，以及是否抛出异常。Acegi Security对方法调用和web请求都提供around advice。我们使用AOP联盟实现对方法调用的around advice，对于web请求的around advice则是使用标准的过滤器（Filter）。</p>
<p>&nbsp; 对于那些不熟悉AOP的人来说，关键是要理解Acegi Security能够帮助你保护方法调用以及web请求。大多数人对保护他们服务层的方法调用感兴趣。这是因为在当前的J2EE应用中，服务层包含了大多数的业务逻辑（声明，作者不赞成这种设计，反而支持正确封装的领域模型以及DTO，assembly, facade 以及 transparent persistence patterns，而不是当前主流的贫血模型，我们将在这里讨论）。如果你需要保护service层的方法调用，使用标准的Spring AOP平台（或者被成为AOP 联盟（AOP Alliance））就足够了。如果你需要直接对领域模型进行保护，那么可以考虑使用AspectJ。</p>
<p>&nbsp;&nbsp;&nbsp; 你可以选择对使用AspectJ 或者AOP联盟（AOP Alliance）对方法进行授权，或者你可以选择使用过滤器（filter）来对web请求进行授权。你将0个，1个，2个或者3个这些方法一起使用。主流的用法是执行一些web请求授权，以及在服务层使用AOP联盟（AOP Alliance）对一些方法调用授权。</p>
<p>&nbsp;&nbsp;&nbsp; Acegi Security使用"安全对象"（secure object）这个词来指任何能够将安全应用于其上的对象。每个Acegi Security支持的安全对象都有自己的类，它是AbstractSecurityInterceptor的子类。重要的一点是，如果一个principal通过认证，当AbstractSecurityInterceptor执行的时候，SecurityContextHolder中要包含一个有效的Authentication。</p>
<p>&nbsp;&nbsp;&nbsp; AbstractSecurityInterceptor提供一个固定的工作流程来处理安全对象请求。这个工作流程包括查找和当前请求相关联的"配置属性（configuration attributes）"。配置属性（configuration attributes）可以被认为是对被AbstractSecurityInterceptor使用的类有特殊含义的字符串。他们通常针对AbstractSecurityInterceptor使用XML进行配置。反正，AbstractSecurityInterceptor会询问AccessDecisionManager "这是配置属性（configuration attributes），这是当前的认证对象（Authentication object），这是当前请求的详细信息－那么这个特定的principal可以执行这个特定的操作吗？"。</p>
<p>&nbsp;&nbsp;&nbsp; 假如AccessDecisionManager判定允许这个请求，那么AbstractSecurityInterceptor一般来说就继续执行请求。虽然这样，用户在少数情况之下可能需要替换SecurityContext中的Authentication，可以通过AccessDecisionManager调用一个RunAsManager来实现。在某些不常见的情形下这将非常有用，例如服务层的方法需要用另一种标识（身份）来调用远程系统。这可能有所帮助，因为Acegi Security自动在不同的服务器之间传播安全标识（假设你正确配置了RMI或者HttpInvoker remoting protocol client）。</p>
<p>&nbsp;&nbsp;&nbsp; 随着安全对象处理和返回－意味着方法调用完毕或者过滤器链（filter chain）处理完毕－AbstractSecurityInterceptor有最后的机会来处理调用。这时，AbstractSecurityInterceptor可能会修改返回的对象。我们可能要这样做，因为授权判断不能在安全对象调用途中执行。由于高度的可插拔性，如果需要AfterInvocationManager将控制权交给AfterInvocationManager来实际修改对象。这个类甚至可以彻底替换对象，或者抛出异常，或者根本不修改它。</p>
<p>&nbsp;&nbsp;&nbsp; 因为是AbstractSecurityInterceptor中心模版类，看起来第一副插图该献给它。（译注：原手册里的图画的太丑陋了，我用jude重新画了一遍）</p>
<p><img alt="" src="http://wiki.springside.org.cn/download/attachments/1629/ch2-1.jpg" align=absMiddle border=0>&nbsp;</p>
<p>图1 关键"安全对象"模型</p>
<p>&nbsp;&nbsp;&nbsp; 只有那些希望实现全新的对请求进行截取截取和授权方式的开发者才需要直接使用安全对象。例如，可能构建一个新的安全对象安全调用一个消息系统。任何需要安全并且能够提供一种截取调用的方式(例如AOP around advice semantics)的东西都可以成为安全对象。虽然如此，大部分的Spring应用都会只是透明应用当前支持的三种安全对象类型（AOP Alliance MethodInvocation, AspectJ JoinPoint 和 web request FilterInterceptor）。</p>
<h2><a name=Chapter2.TechnicalOverview-2.5.%E7%BB%93%E8%AE%BA></a><strong>2.5.</strong> <strong>结论</strong></h2>
<p>&nbsp;&nbsp;&nbsp; 恭喜！你已经获取了Acegi Security足够的概括性的图景来开始着手你的项目。我们探究了共享组件，认证过程，以及对"安全对象"的通用授权概念。手册中的余下部分你可能用到也可能用不到，可以按照任意顺序阅读。</p>
<h1><a name=Chapter3.SupportingInfrastructure-%E7%AC%AC%E4%B8%89%E7%AB%A0.%E5%8D%8F%E5%8A%A9%E7%B3%BB%E7%BB%9F></a>第三章. 协助系统</h1>
<p>本章介绍一些Acegi Security使用的附加和协助系统。那些和安全无关，但是包含在Acegi Security项目中的部分，将会在本章中讨论</p>
<h2><a name=Chapter3.SupportingInfrastructure-3.1.%E6%9C%AC%E5%9C%B0%E5%8C%96></a>3.1. 本地化</h2>
<p>Acegi Security支持对终端客户可能会看到的异常信息进行本地化。如果你的应用是为英文用户设计的，那么你什么都不用做，因为Acegi Security的所有消息默认都是英文的。如果你要支持其他区域用户，那么本节包含了你所需要了解的所有东西。</p>
<p>包括认证失败或者访问被拒绝（授权失败）的所有异常消息都可以被本地化。提供给开发者或者系统部署人员的异常或者日志信息(包括错误的属性、接口不符、构造器错误、debug级日志)没有被本地化，它们硬编码在Acegi Security的代码中。</p>
<p>在acegi-security-xx.jar（译注：xx代表版本号）的org.acegisecurity包中包含了一个messages.properties文件。这个文件会被你的application context引用，因为Acegi Security实现了Spring的MessageSourceAware接口，它期待在application context启动的时候注入一个message resolver。通常你所需要做的是在你的application context中注册一个引用这个消息的bean，如下所示：</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>&lt;bean id=<span class=code-quote>"messageSource"</span> class=<span class=code-quote>"org.springframework.context.support.ReloadableResourceBundleMessageSource"</span>&gt;&lt;property name=<span class=code-quote>"basename"</span>&gt;&lt;value&gt;org/acegisecurity/messages&lt;/value&gt;&lt;/property&gt;&lt;/bean&gt;</pre>
</div>
</div>
<p>messages.properties是按照资源包标准命名的，它代表了Acegi Securtiy支持的默认语言。文件默认是英文的。如果你不注册一个消息源，Acegi Security仍然可以正常工作，它会用回硬编码的英文消息。</p>
<p>如果你想定制messages.properties文件，或者支持其他语言，那么你应该copy这个文件，然后重命名，并在上述的bean定义中注册。因为文件中的key并不多，因此本地化花不了多少工夫。如果你针对消息文件进行了本地化，那么请和社区分享，你可以添加一个JIRA任务，将你正确命名的messages.properties本地化文件作为附件添加。</p>
<p>为了完善关于本地化的讨论需要知道Spring的ThreadLocal org.springframework.context.i18n.LocaleContextHolder。你应该为每个用户设置代表他区域的LocaleContextHolder。Acegi Security会尝试从这个ThreadLocal中获取的Locale来从消息源中获取消息。请参考Spring的文档以获取更多使用LocaleContextHolder和能够帮你自动设置它的辅助类(例如</p>
<p>AcceptHeaderLocaleResolver, CookieLocaleResolver, FixedLocaleResolver, SessionLocaleResolver 等)的详细信息。</p>
<h2><a name=Chapter3.SupportingInfrastructure-3.2.Filters></a>3.2. Filters</h2>
<p>正如你在整个手册中看到的那样，Acegi Security使用很多filter。你可以使用FilterToBeanProxy或者FilterChainProxy来确定这些是怎样加入到你的web应用中的，下面我们来看看。</p>
<p>大部分filter使用FilterToBeanProxy来配置。例如下面web.xml中配置所示：</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>&lt;filter&gt;&nbsp;&nbsp;&nbsp; &lt;filter-name&gt;Acegi HTTP Request Security Filter&lt;/filter-name&gt;&nbsp;&nbsp;&nbsp; &lt;filter-class&gt;org.acegisecurity.util.FilterToBeanProxy&lt;/filter-class&gt;&nbsp;&nbsp;&nbsp; &lt;init-param&gt;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;param-name&gt;targetClass&lt;/param-name&gt;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;param-value&gt;org.acegisecurity.ClassThatImplementsFilter&lt;/param-value&gt;&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;&lt;/filter&gt;</pre>
</div>
</div>
<p>注意在web.xml中的filter实际上是一个FilterToBeanProxy，而不是真正实现filter逻辑的filter。FilterToBeanProxy所作的是代理Filter的方法到一个从Spring的application context 获取的bean。这使得这个bean可以享受Spring application context的生命周期支持以及配置灵活性。这个bean必须实现javax.servlet.Filter。</p>
<p>FilterToBeanProxy只需要一个简单的初始化参数，targetClass或者targetBean。targetClass会定位application context中指定的类的第一个对象，而FilterToBeanProxy按照bean的名字定位对象。象标准的Spring web应用一样，FilterToBeanProxy使用WebApplicationContextUtils.getWebApplicationContext(ServletContext)来访问application context，所以你应该在web.xml中配置一个ContextLoaderListener。</p>
<p>在IoC容器而不是servlet容器中部署Filter会有一个生命周期的问题。特别是，哪个容器应该负责调用Filter的"startup" 和 "shutdown"方法？注意到Filter的初始化和析构顺序随servlet容器不同而不同，如果一个Filter依赖于由另一个更早初始化的Filter的配置，这样就会出现问题。另一方面，Spring IoC具备更加完善的生命周期/IoC接口（例如InitializingBean, DisposableBean, BeanNameAware, ApplicationContextAware以及其他许多）以及一个容易理解的接口契约（interface contract），可预见的方法调用顺序，自动装配支持，以及可以避免实现Spring接口的选项（例如Spring XML中的destroy-method 属性）。因此，我们推荐尽可能使用Spring生命周期服务而不是servlet容器生命周期服务。FilterToBeanProxy默认不会将init(FilterConfig) 和 destroy()方法委派到被代理的bean。如果你需要这些调用被委派，那么将lifecycle初始化参数设置为servlet-container-managed。</p>
<p>我们强烈推荐你使用FilterChainProxy而不是FilterToBeanProxy。虽然FilterToBeanProxy是一个非常有用的类FilterToBeanProxy，问题是当web.xml中filter变多时，&lt;filter&gt; 和 &lt;filter-mapping&gt;项就会太多而变得臃肿不堪。为了解决这个问题，Acegi Security提供一个FilterChainProxy类。它在FilterToBeanProxy中被装配（正如上面例子中所示），但目标类（target class）是org.acegisecurity.util.FilterChainProxy。这样过滤器链（filter chain）可以在application context中按照如下代码配置：</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>&lt;bean id=<span class=code-quote>"filterChainProxy"</span> class=<span class=code-quote>"org.acegisecurity.util.FilterChainProxy"</span>&gt;&nbsp;&nbsp;&nbsp; &lt;property name=<span class=code-quote>"filterInvocationDefinitionSource"</span>&gt;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;value&gt;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; PATTERN_TYPE_APACHE_ANT&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /webServices/*=httpSessionContextIntegrationFilterWithASCFalse,basicProcessingFilter,exceptionTranslationFilter,&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /*=httpSessionContextIntegrationFilterWithASCTrue,authenticationProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/value&gt;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&lt;/bean&gt;</pre>
</div>
</div>
<p>你可能注意到FilterSecurityInterceptor定义方式的相似之处。同时支持正则表达式和Ant Paths格式，越对应的URI越早出现。在运行时，FilterChainProxy会定位符合当前的web请求的第一个URI模式。每个对应的配置属性代表了在application context中定义的一个bean的名字。接着fiter会按照它们被指定的顺序，按照FilterChain的标准行为模式被调用(如果一个Filter决定停止处理，它可以不在chain中执行)。</p>
<p>如你所见，FilterChainProxy需要为不同的请求模式重复配置filter的名字（在上面的例子中，, exceptionTranslationFilter 和 filterSecurityInterceptor 是重复的）。这样的设计是为了让FilterChainProxy能够为不同的URI配置不同的filter调用顺序，同时也提高了表达力（针对正则表达式、Ant Paths、以及任何FilterInvocationDefinitionSource的特定实现）和清晰度，可以知道是哪个filter应该被调用。</p>
<p>你可能注意到了我们在filter chain定义了两个HttpSessionContextIntegrationFilter (ASC是allowSessionCreation的缩写,是HttpSessionContextIntegrationFilter的一个属性)。因为web服务不会为将来的请求提供一个jsessionid，为这样的用户创建HttpSessions是浪费的。如果你有一个需要最大限度的伸缩性的高容量的应用，我们建议你使用上述的方法。对于小的应用，使用单一的HttpSessionContextIntegrationFilter (默认的allowSessionCreation设为true)应该足够了。</p>
<p>说到生命周期问题，如果对FilterChainProxy自身调用init(FilterConfig) 和 destroy()方法，它会把它代理到底层的filter。这样FilterChainProxy保证只初始化和析构每个filter一次，不论它在FilterInvocationDefinitionSource中定义了多少次。你可以通过FilterToBeanProxy的lifecycle初始化参数来控制这些方法是否被调用。如上面所讨论的那样，默认所有servlet容器生命周期调用是不被代理到FilterChainProxy的。</p>
<p>在web.xml中定义的filter的顺序是非常重要的。不管你实际用到哪个filter，&lt;filter-mapping&gt;的顺序应该是如下所示的：</p>
<p>1．ChannelProcessingFilter，因为可能要重定向到另一种协议。</p>
<p>2．ConcurrentSessionFilter 因为不使用任何SecurityContextHolder的功能，但是需要更新SessionRegistry来表示当前的发送请求的principal。</p>
<p>3． HttpSessionContextIntegrationFilter, 这样当一个web请求开始的时候就可以在SecurityContextHolder中设置一个SecurityContext，当web请求结束的时候任何对SecurityContext的改动都会被copy到HttpSession（以备下一个web请求使用）。</p>
<p>4．Authentication processing mechanisms - AuthenticationProcessingFilter, CasProcessingFilter, BasicProcessingFilter, HttpRequestIntegrationFilter, JbossIntegrationFilter 等 - 修改SecurityContextHolder，使其中包含一个有效的认证请求令牌（token）。</p>
<p>5．SecurityContextHolderAwareRequestFilter, 如果你使用它来在你的servlet容器中安装一个Acegi Security aware HttpServletRequestWrapper。</p>
<p>6．RememberMeProcessingFilter, 如果早期的认证处理过程没有更新SecurityContextHolder，并且请求（request）提供了一个cookie启用remember-me服务，一个合适的被记住的Authentication对象会被放到SecurityContextHolder那里。</p>
<p>7．AnonymousProcessingFilter, 如果早期的认证处理过程没有更新SecurityContextHolder，, 一个匿名Authentication 对象会被放到SecurityContextHolder那里。</p>
<p>8．ExceptionTranslationFilter, 捕获所有的Acegi Security 异常，这样要么返回一个HTTP错误响应或者加载一个对应的AuthenticationEntryPoint。</p>
<p>9．FilterSecurityInterceptor, 保护 web URIs</p>
<p>所有上述的filter使用FilterToBeanProxy或FilterChainProxy。建议在一个应用中使用一个单个的FilterToBeanProxy代理到一个单个的FilterChainProxy。，在FilterChainProxy中定义所有的Acegi Security Filters。如果你使用SiteMesh，确保Acegi Security filters 在 SiteMesh filters调用前调用。这样使SecurityContextHolder在SiteMesh decorator使用前能够及时被装配。</p>
<h1><a name=Chapter4.ChannelSecurity-%E7%AC%AC%E5%9B%9B%E7%AB%A0.%E4%BF%A1%E9%81%93%E5%AE%89%E5%85%A8></a>第四章. 信道安全</h1>
<h2><a name=Chapter4.ChannelSecurity-4.1.%E6%A6%82%E8%BF%B0></a>4.1. 概述</h2>
<p>Acegi Security不仅能满足你的认证和授权的请求，而且能够保证你的未认证的web请求也能拥有某些属性。这些属性可能包括使用特定的传输类型，在HttpSession设置特定的属性等等。Web请求的最普遍的需求是使用特定的传输协议，例如HTTPS。</p>
<p>在传输安全中的一个重要议题就是会话劫持（session hijacking）。Web容器通过一个jsessionid来引用一个HttpSession，这个jsessionid通过cookie 或者URL重写转向（URL rewriting）发送到到客户端。如果jsessionid是通过HTTP发送的，那么就存在被劫持以及在认证过程之后冒充被认证用户的可能。这是因为大部分的web容器为特定的用户维护同一个会话标识符，即便是用户从HTTP 切换到 HTTPS页面。</p>
<p>如果对于你的特定应用来说，会话劫持（session hijacking）是一个很严重的风险，那么唯一的解决方法就是对每一个请求都使用HTTPS。这意味着jsessionid不会使用非安全信道传输。你要保证你的web.xml中定义&lt;welcome-file&gt;，把它指向一个HTTPS位置，同时应用程序不把用户指向一个HTTP位置。Acegi Security提供一个解决方案帮助你实现后者。</p>
<h2><a name=Chapter4.ChannelSecurity-4.2.%E9%85%8D%E7%BD%AE></a>4.2. 配置</h2>
<p>启用Acegi Security的信道安全服务，需要在web.xml中增加如下行：</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>&lt;filter&gt;&lt;filter-name&gt;Acegi Channel Processing Filter&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.securechannel.ChannelProcessingFilter&lt;/param-value&gt;&lt;/init-param&gt;&lt;/filter&gt;&lt;filter-mapping&gt;&lt;filter-name&gt;Acegi Channel Processing Filter&lt;/filter-name&gt;&lt;url-pattern&gt;/*&lt;/url-pattern&gt;&lt;/filter-mapping&gt;</pre>
</div>
</div>
<p>和平时一样，你同样需要在application context中配置filter</p>
<div class=code>
<div class=codeContent>
<pre class=code-java>&lt;bean id=<span class=code-quote>"channelProcessingFilter"</span> class=<span class=code-quote>"org.acegisecurity.securechannel.ChannelProcessingFilter"</span>&gt;&lt;property name=<span class=code-quote>"channelDecisionManager"</span>&gt;&lt;ref bean=<span class=code-quote>"channelDecisionManager"</span>/&gt;&lt;/property&gt;&lt;property name=<span class=code-quote>"filterInvocationDefinitionSource"</span>&gt;&lt;value&gt;CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON\A/secure/.*\Z=REQUIRES_SECURE_CHANNEL\A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL\A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL\A.*\Z=REQUIRES_INSECURE_CHANNEL&lt;/value&gt;&lt;/property&gt;&lt;/bean&gt;&lt;bean id=<span class=code-quote>"channelDecisionManager"</span> class=<span class=code-quote>"org.acegisecurity.securechannel.ChannelDecisionManagerImpl"</span>&gt;&lt;property name=<span class=code-quote>"channelProcessors"</span>&gt;&lt;list&gt;&lt;ref bean=<span class=code-quote>"secureChannelProcessor"</span>/&gt;&lt;ref bean=<span class=code-quote>"insecureChannelProcessor"</span>/&gt;&lt;/list&gt;&lt;/property&gt;&lt;/bean&gt;&lt;bean id=<span class=code-quote>"secureChannelProcessor"</span> class=<span class=code-quote>"org.acegisecurity.securechannel.SecureChannelProcessor"</span>/&gt;&lt;bean id=<span class=code-quote>"insecureChannelProcessor"</span> class=<span class=code-quote>"org.acegisecurity.securechannel.InsecureChannelProcessor"</span>/&gt;</pre>
</div>
</div>
<p>ChannelProcessingFilter和FilterSecurityInterceptor一样支持Apache Ant style paths。</p>
<p>ChannelProcessingFilter的工作方式是过滤所有的web请求，并将判断将适合的配置属性应用于其上。然后它代理到ChannelDecisionManager。默认的实现类ChannelDecisionManagerImpl应该能够满足大多数需求。它就代理到配置好的ChannelProcessor实例列表。ChannelProcessor会检视请求，如果它不满意请求（例如请求是发送自不正确的传输协议）它将会重定向，抛出异常或者采取其他任何恰当的措施。</p>
<p>Acegi Security 包括ChannelProcessor两个实体类实现：SecureChannelProcessor 保证配置了REQUIRES_SECURE_CHANNEL 属性的请求都是从HTTPS发送过来的。而InsecureChannelProcessor 保证配置了REQUIRES_INSECURE_CHANNEL 属性的请求都是从HTTP发送过来的。如果没有使用请求的协议，这两个实现都会转到ChannelEntryPoint，而两个ChannelEntryPoint 实现所作的就是简单的把请求相应按照HTTP 和 HTTPS重定向。</p>
<p>要注意重定向是绝对（例如<span class=nobr><a title="Visit page outside Confluence" href="http://www.company.com:8080/app/page" rel=nofollow><u><font color=#0000ff>http://www.company.com:8080/app/page<sup><img class=rendericon height=7 alt="" src="http://wiki.springside.org.cn/images/icons/linkext7.gif" width=7 align=absMiddle border=0></sup></font></u></a></span>）而不是相对的(例如 /app/page)。在测试中发现Internet Explorer 6 Service Pack 1 有一个bug，因此如果在重定向的时候也改变使用的端口，它就不能正确响应。对应这个bug，在很多Acegi Security bean中都会使用的PortResolverImpl也使用绝对URL。请参阅PortResolverImpl的JavaDoc以获取更多信息。</p>
<p>你要注意使用为了在登录过程中保证用户名和密码的安全，要使用安全信道。如果你配合基于表单的登录使用ChannelProcessingFilter，请记得一定要把你的登录页面设置为REQUIRES_SECURE_CHANNEL，并且AuthenticationProcessingFilterEntryPoint.forceHttps属性设置为true。</p>
<h2><a name=Chapter4.ChannelSecurity-4.3.%E7%BB%93%E8%AE%BA></a>4.3. 结论</h2>
<p>一旦配置好了，使用安全信道是非常简单的。只要请求页面，不用管使用什么协议（HTTP 或 HTTPS）或什么端口（80, 8080, 443, 8443等）。显然你只要确定初始请求(获取通过在web.xml 中的&lt;welcome-file&gt; 或一个众所周知的主页URL)，完成以后filter会执行你application context定义的重定向。</p>
<p>你也可以在ChannelDecisionManagerImpl中增加自己的ChannelProcessor实现。例如，你可能通过"输入图片中的内容"检测到一个个人类用户，然后在HttpSession中设置一个属性。</p>
<p>要判断一个安全检查应该是或者ChannelProcessor或是 AccessDecisionVoter 记得前者是设计用来处理认证或者未认证的请求，而后者是设计用来处理已认证的请求。因此后者可以访问已认证的principal被授予的权限。</p>
<p>另外，ChannelProcessor检测到问题后一般是引发一个HTTP/HTTPS重定向这样他的请求可以被满足，而AccessDecisionVoter将则会跑出一个AccessDeniedException异常(取决于支配的 AccessDecisionManager)。</p>
<p>文章来源:<a href="http://wiki.springside.org.cn/display/springside/Acegi+Reference+Preface"><font color=#006699 size=3><u>http://wiki.springside.org.cn/display/springside/Acegi+Reference+Preface</u></font></a>&nbsp;</p>
</div>
<img src ="http://www.blogjava.net/junky/aggbug/126772.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-06-28 12:42 <a href="http://www.blogjava.net/junky/archive/2007/06/28/126772.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi Security -- Spring下最优秀的安全系统 </title><link>http://www.blogjava.net/junky/archive/2007/06/28/126747.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Thu, 28 Jun 2007 02:50:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/06/28/126747.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/126747.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/06/28/126747.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/126747.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/126747.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 一&nbsp;Acegi安全系统介绍&nbsp;&nbsp;&nbsp; Author: cac 差沙 &nbsp;&nbsp;&nbsp; Acegi是Spring Framework 下最成熟的安全系统，它提供了强大灵活的企业级安全服务，如完善的认证和授权机制，Http资源访问控制，Method 调用访问控制，Access Control List (ACL) 基于对象实例的访问控制，...&nbsp;&nbsp;<a href='http://www.blogjava.net/junky/archive/2007/06/28/126747.html'>阅读全文</a><img src ="http://www.blogjava.net/junky/aggbug/126747.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-06-28 10:50 <a href="http://www.blogjava.net/junky/archive/2007/06/28/126747.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi中的ACL用来解决什么问题？</title><link>http://www.blogjava.net/junky/archive/2007/06/27/126611.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Wed, 27 Jun 2007 08:02:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/06/27/126611.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/126611.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/06/27/126611.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/126611.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/126611.html</trackback:ping><description><![CDATA[ACL要解决什么问题？Ben Alex在Acegi1.0.3中重新设计了这一部分，从侧面说明了其在权限控制中的重要性。下面这段话清楚地解释了ACL的用途：
<div>&nbsp;&nbsp;&nbsp; Complex applications often will find the need to define access permissions not simply at a web request or<br>method invocation level. Instead, security decisions need to comprise both who (Authentication), where<br>(MethodInvocation) and what (SomeDomainObject). In other words, authorization decisions also need to<br>consider the actual domain object instance subject of a method invocation.</div>
<img src ="http://www.blogjava.net/junky/aggbug/126611.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-06-27 16:02 <a href="http://www.blogjava.net/junky/archive/2007/06/27/126611.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Acegi安全系统扩展 ----- Acegi ACL使用</title><link>http://www.blogjava.net/junky/archive/2007/06/27/126601.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Wed, 27 Jun 2007 07:13:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/06/27/126601.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/126601.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/06/27/126601.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/126601.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/126601.html</trackback:ping><description><![CDATA[<table style="TABLE-LAYOUT: fixed">
    <tbody>
        <tr>
            <td>
            <div class=cnt>
            <h3>4.1 基本概念</h3>
            <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在google中搜索'acl'会找到很多相关的介绍，而且涉及的范围也特别广泛。ACL是(Access Control List)的缩写，顾名思义，ACL是&#8216;访问控制列表&#8217;的意思。通俗点说，ACL保存了所有用户或角色对资源的访问权限。最典型的ACL实现是流行操作系统(window, unix)的文件访问控制系统，精确定义了某个用户或角色对某个特定文件的读、写、执行等权限，更通俗的例子是可以定义某个管理员只能管一部分的订单，而另一个管理员只能管另一部分的。</p>
            <h3>4.2 Acegi ACL配置</h3>
            <p>Acegi好早就实现了ACL（好像是0.5），但是使用起来确实有点麻烦，所以用的不是太广泛。这里简单的说明一下使用方法，希望有更多的朋友来试试。<br><br>首先要理解Acegi里面Voter的概念，ACL正是在一个Voter上扩展起来的。现来看一下AclVoter的配置。<br><br></p>
            <div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean </span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="aclBeanReadVoter"</span><span style="COLOR: #ff0000"> class</span><span style="COLOR: #0000ff">="org.acegisecurity.vote.BasicAclEntryVoter"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="processConfigAttribute"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">value</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">ACL_READ</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">value</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="processDomainObjectClass"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">value</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000">org.springside.modules.security.acl.domain.AclDomainAware</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">value</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="aclManager"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref </span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="aclManager"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="requirePermission"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref </span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref </span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="org.acegisecurity.acl.basic.SimpleAclEntry.READ"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span></div>
            <ol>
                <li><span style="COLOR: #000000">ACL_READ指的是这个Voter对哪些SecurityConfig起作用，我们可以把<span style="COLOR: #000000">ACL_READ配置在想要拦截的Method上。比方说我们要拦截readOrder这个方法，以实现ACL控制，可以这样配置。<br><em><font color=#006400>orderManager.readOrder=ACL_READ</font></em></span></span>
                <li><span style="COLOR: #000000"><span style="COLOR: #000000"><font color=#000000>processDomainObjectClass指出哪些DomainObject是要进行ACL校验的。</font></span></span>
                <li><span style="COLOR: #000000"><span style="COLOR: #000000"><font color=#000000>aclManager是一个比较重要的概念，主要负责在权限列表中根据用户和DomainObject取得acl列表。</font></span></span>
                <li><span style="COLOR: #000000"><span style="COLOR: #000000">requirePermission指出要进行这个操作必须具备的acl权限，比方说read操作就必须有<font color=#000000>ADMINISTRATION或READ两个权限。</font></span></span> </li>
            </ol>
            <p><span style="COLOR: #000000"><span style="COLOR: #000000">其实整个过程看下来比较清晰，下面来看一下AclManager如何配置。<br></span></span></p>
            <div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">&lt;!--</span><span style="COLOR: #008000"> ========= ACCESS CONTROL LIST LOOKUP MANAGER DEFINITIONS ========= </span><span style="COLOR: #008000">--&gt;</span><span style="COLOR: #000000"><br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean </span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="aclManager"</span><span style="COLOR: #ff0000"> class</span><span style="COLOR: #0000ff">="org.acegisecurity.acl.AclProviderManager"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="providers"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref </span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="basicAclProvider"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean </span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="basicAclProvider"</span><span style="COLOR: #ff0000"> class</span><span style="COLOR: #0000ff">="org.acegisecurity.acl.basic.BasicAclProvider"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="basicAclDao"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref </span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="basicAclExtendedDao"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean </span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="basicAclExtendedDao"</span><span style="COLOR: #ff0000"> class</span><span style="COLOR: #0000ff">="org.acegisecurity.acl.basic.jdbc.JdbcExtendedDaoImpl"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="dataSource"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref </span><span style="COLOR: #ff0000">bean</span><span style="COLOR: #0000ff">="dataSource"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span></div>
            <p><br>很明显ACLManager继承了Acegi的一贯风格，Provider可以提供多种取得ACL访问列表的途径，默认的是用<font color=#0000ff>basicAclProvider</font><font color=#000000>在数据库中取得。既然提到了数据库，那我们就来看一下Acegi默认提供的ACL在数据库里的保存表结构：<br><br></font>
            <ol>
                <li>acl_object_identity表存放了所有受保护的domainObject的信息。其中object_identity字段保存了domainObject的class和id，默认的保存格式是：domainClass:domainObjectId。
                <li>acl_permission 就是ACL权限列表了，recipient 是用户或角色信息，mask表示了这个用户或角色对这个domainObject的访问权限。注意这些信息的保存格式都是可以根据自己的需要改变的。 </li>
            </ol>
            <p>这样读取和删除的时候Acegi就能很好的完成拦截工作，但是读取一个List的时候，如何才能把该用户不能操作的domainObject剔除掉呢？这就需要afterInvocationManager来完成这个工作。下面来看下配置：<br></p>
            <div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">&lt;!--</span><span style="COLOR: #008000"> ============== "AFTER INTERCEPTION" AUTHORIZATION DEFINITIONS =========== </span><span style="COLOR: #008000">--&gt;</span><span style="COLOR: #000000"><br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean </span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="afterInvocationManager"</span><span style="COLOR: #ff0000"> class</span><span style="COLOR: #0000ff">="org.acegisecurity.afterinvocation.AfterInvocationProviderManager"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="providers"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref </span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="afterAclCollectionRead"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #008000">&lt;!--</span><span style="COLOR: #008000"> Processes AFTER_ACL_COLLECTION_READ configuration settings </span><span style="COLOR: #008000">--&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean </span><span style="COLOR: #ff0000">id</span><span style="COLOR: #0000ff">="afterAclCollectionRead"</span><span style="COLOR: #ff0000"> class</span><span style="COLOR: #0000ff">="org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationCollectionFilteringProvider"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="aclManager"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref </span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="aclManager"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">property </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="requirePermission"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref </span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">ref </span><span style="COLOR: #ff0000">local</span><span style="COLOR: #0000ff">="org.acegisecurity.acl.basic.SimpleAclEntry.READ"</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">list</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">property</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">bean</span><span style="COLOR: #0000ff">&gt;</span></div>
            <p><br><font color=#000000>afterAclCollectionRead会在拦截的方法执行结束的时候执行。主要的作用就是在返回的List中挨个检查domainObject的操作权限，然后根据requirePermission来剔除不符合的domainObject。</font></p>
            <h3>4.3 使用RuleEngine设置的ACL规则</h3>
            <p>在SpringSide里使用了RuleEngine来设置ACL规则，具体规则见<br>bookstore-sample\resources\rules\drl</p>
            <p>&#160;</p>
            <div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #0000ff">CREATE</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">TABLE</span><span style="COLOR: #000000"> acl_object_identity (<br>id </span><span style="COLOR: #ff00ff">IDENTITY</span><span style="COLOR: #000000"> </span><span style="COLOR: #808080">NOT</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">NULL</span><span style="COLOR: #000000">,<br>object_identity VARCHAR_IGNORECASE(</span><span style="FONT-WEIGHT: bold; COLOR: #800000">250</span><span style="COLOR: #000000">) </span><span style="COLOR: #808080">NOT</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">NULL</span><span style="COLOR: #000000">,<br>parent_object </span><span style="FONT-WEIGHT: bold; COLOR: #000000">INTEGER</span><span style="COLOR: #000000">,<br>acl_class VARCHAR_IGNORECASE(</span><span style="FONT-WEIGHT: bold; COLOR: #800000">250</span><span style="COLOR: #000000">) </span><span style="COLOR: #808080">NOT</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">NULL</span><span style="COLOR: #000000">,<br></span><span style="COLOR: #0000ff">CONSTRAINT</span><span style="COLOR: #000000"> unique_object_identity </span><span style="COLOR: #0000ff">UNIQUE</span><span style="COLOR: #000000">(object_identity),<br></span><span style="COLOR: #0000ff">FOREIGN</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">KEY</span><span style="COLOR: #000000"> (parent_object) </span><span style="COLOR: #0000ff">REFERENCES</span><span style="COLOR: #000000"> acl_object_identity(id)<br>);<br></span><span style="COLOR: #0000ff">CREATE</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">TABLE</span><span style="COLOR: #000000"> acl_permission (<br>id </span><span style="COLOR: #ff00ff">IDENTITY</span><span style="COLOR: #000000"> </span><span style="COLOR: #808080">NOT</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">NULL</span><span style="COLOR: #000000">,<br>acl_object_identity </span><span style="FONT-WEIGHT: bold; COLOR: #000000">INTEGER</span><span style="COLOR: #000000"> </span><span style="COLOR: #808080">NOT</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">NULL</span><span style="COLOR: #000000">,<br>recipient VARCHAR_IGNORECASE(</span><span style="FONT-WEIGHT: bold; COLOR: #800000">100</span><span style="COLOR: #000000">) </span><span style="COLOR: #808080">NOT</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">NULL</span><span style="COLOR: #000000">,<br>mask </span><span style="FONT-WEIGHT: bold; COLOR: #000000">INTEGER</span><span style="COLOR: #000000"> </span><span style="COLOR: #808080">NOT</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">NULL</span><span style="COLOR: #000000">,<br></span><span style="COLOR: #0000ff">CONSTRAINT</span><span style="COLOR: #000000"> unique_recipient </span><span style="COLOR: #0000ff">UNIQUE</span><span style="COLOR: #000000">(acl_object_identity, recipient),<br></span><span style="COLOR: #0000ff">FOREIGN</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">KEY</span><span style="COLOR: #000000"> (acl_object_identity) </span><span style="COLOR: #0000ff">REFERENCES</span><span style="COLOR: #000000"> acl_object_identity(id)<br>);</span></div>
            </div>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/junky/aggbug/126601.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-06-27 15:13 <a href="http://www.blogjava.net/junky/archive/2007/06/27/126601.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CAS 使用 ESUP 插件认证 LDAP 用户的单点登录(转)</title><link>http://www.blogjava.net/junky/archive/2007/06/05/122165.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Tue, 05 Jun 2007 08:35:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/06/05/122165.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/122165.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/06/05/122165.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/122165.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/122165.html</trackback:ping><description><![CDATA[<p>ESUP为CAS的SSO提供了LDAP用户模式，本文详细论述了使用LDAP认证的配置方式，它主要分为两种主要形式：快速绑定和搜寻模式。在实际使用时，我使用了更为通用的搜寻模式。</p>
<p>前面的一篇文章我曾经介绍了使用Tomcat简单部署CAS的方法，但对于用户名和密码只是做相同性的校验匹配验证，下面我将一步步将其改变为使用Sun Directory Server进行用户认证的方式。</p>
<ol>
    <li>下载esup-casgeneric-2.0.5-2，将其部署到CAS源代码中。方法很简单，修改其properties文件，指定CAS源代码的存储位置，在我机器中我将其指向了eclipse的workspace工作目录中的CAS应用。随后使用ant编译其build.xml脚本，实际上运行该教本只是做了一个复制拷贝工作，将ESUP的源代码文件和配置文件复制到CAS源码中。
    <li>修改原有CAS的认证方式，在web.xml中，将CAS的认证控制器由默认的edu.yale.its.tp.cas.auth.provider.SampleHandler改为ESUP的GenericHandler，修改后的内容如下：<br>&lt;context-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;edu.yale.its.tp.cas.authHandler&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;org.esupportail.cas.server.GenericHandler&lt;/param-value&gt;<br>&lt;/context-param&gt;
    <li>修改ESUP的配置文件genericHandler.xml，将其默认的控密码和相同性验证方式改为LDAP验证方式，并同时配置LDAP相关信息，下面是修改前与修改后的内容：<br>修改前：<br>&lt;authentication empty_password_accepted="on" debug="on"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;handler&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;classname&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; org.esupportail.cas.server.handlers.test.EmptyPasswordHandler&lt;/classname&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/handler&gt;<br>&nbsp;&nbsp;&nbsp; &lt;handler&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;classname&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; org.esupportail.cas.server.handlers.test.PasswordEqualsUsernameHandler&lt;/classname&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/handler&gt;<br>&lt;/authentication&gt;<br>修改后：<br>&lt;authentication debug="off"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;handler&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;classname&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; org.esupportail.cas.server.handlers.ldap.BindLdapHandler&lt;/classname&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;config&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;search_base&gt;ou=People,dc=sjtu,dc=edu,dc=cn&lt;/search_base&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter&gt;uid=%u&lt;/filter&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;scope&gt;sub&lt;/scope&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bind_dn&gt;cn=Directory Manager&lt;/bind_dn&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bind_password&gt;password&lt;/bind_password&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;server&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;url&gt;ldap://yuanxz.sjtu.edu.cn:389/&lt;/url&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/server&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;disable_multiple_accounts/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/config&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/handler&gt;<br>&lt;/authentication&gt;
    <li>修改ESUP日志记录配置文件LoggerConf.xml，修改名为File的参数，将其value指向希望的日志存储位置：&lt;param name="File" value="c:/tomcat/logs/esup-casgeneric.log" /&gt;
    <li>重新部署CAS应用到Tomcat，即再次运行build.xml的ant教本，重新启动Tomcat，进入Servlet-Examples的应用实例，点击执行后浏览器仍自动跳转到CAS的认证登录界面，在用户名和密码栏中输入位于LDAP中用户信息，即可通过验证。
    <li>验证日志文件，打开步骤4中配置的日志文件，可以看到CAS成功地连接了LDAP数据库并通过了验证，日志示例如下：<br>INFO [http-8443-Processor24] root.[] 三月/11 14:04:14 - ESUP-Portail Generic Handler 2.0.5-2, reading configuration file...<br>INFO [http-8443-Processor24] root.[] 三月/11 14:04:15 - Configuration file read without any error.<br>INFO [http-8443-Processor24] root.[] 三月/11 14:04:20 - Authentication succeeded for user `yuanxz'.</li>
</ol>
<h1>LDAP authentication with CAS GH</h1>
<p>As LDAP became a standard for user referencials, authentication on an LDAP directory is the most widely used method nowadays. LDAP authentication configuration consists in specifying:</p>
<ul>
    <li>the <strong>mode</strong> used to access LDAP servers (see bellow);
    <li>an <strong>LDAP server</strong> or a list of LDAP servers (for redundancy); </li>
</ul>
<p>Two access modes are provided (<strong>bind</strong> and <strong>fastbind</strong>), depending on the internal structure of the LDAP directory.</p>
<h2>Direct access mode (fastbind)</h2>
<p>The fastbind method can be used with LDAP directories of which the users' Distinguished Names can be directly deduced from their login name (practically, LDAP directories where all the users are stored at the same hierarchical level).</p>
<p>In this mode, CAS tries to connect to the LDAP directory with the user's DN and the password provided.</p>
<p>One may use:</p>
<pre>&lt;authentication debug="off"&gt;<br>  &lt;handler&gt;<br>    &lt;classname&gt;org.esupportail.cas.server.handlers.ldap.FastBindLdapHandler&lt;/classname&gt;<br>    &lt;config&gt;<br>      &lt;filter&gt;uid=%u,ou=people,dc=esup-portail,dc=org&lt;/filter&gt;<br>      &lt;server&gt;<br>        &lt;url&gt;ldap://ldap.esup-portail.org&lt;/url&gt;<br>      &lt;/server&gt;<br>      &lt;server&gt;<br>        &lt;url&gt;ldap://replica.esup-portail.org&lt;/url&gt;<br>      &lt;/server&gt;<br>&lt;/config&gt;<br>  &lt;/handler&gt;<br>&lt;/authentication&gt;</pre>
<p>When using the ldap_fastbind method, the administrator should only sp&#233;cify the filter to find the users' DN in the directory. The following tokens (similar to ldap_saslauthd) can be used in the filter string:</p>
<ul>
    <li>%% = %
    <li>%u = user
    <li>%U = user portion of %u (%U = test when %u = test@domain.tld)
    <li>%d = domain portion of %u (%d = domain.tld when %u = test@domain.tld)
    <li>%1-9 = domain tokens (%1 = tld, %2 = domain when %d = domain.tld)
    <li>%s = service
    <li>%r = realm </li>
</ul>
<p>The %u token has to be used at minimum for the filter to be useful. Defaults to uid=%u.</p>
<h2>Search mode (bind)</h2>
<p>When users are located in different branches of the directory, deducing the users' DN from their login name is impossible. In this case, the ldap_bind mode should be used. It performs a search in the directory before trying to connect.</p>
<p>One may use:</p>
<pre>&lt;authentication debug="off"&gt;<br>  &lt;handler&gt;<br>    &lt;classname&gt;org.esupportail.cas.server.handlers.ldap.BindLdapHandler&lt;/classname&gt;<br>    &lt;config&gt;<br>      &lt;search_base&gt;ou=people,dc=esup-portail,dc=org&lt;/search_base&gt;<br>      &lt;filter&gt;uid=%u&lt;/filter&gt;<br>      &lt;scope&gt;sub&lt;/scope&gt;<br>      &lt;bind_dn&gt;admin&lt;/bind_dn&gt;<br>      &lt;bind_password&gt;secret&lt;/bind_password&gt;<br>      &lt;server&gt;<br>        &lt;url&gt;ldap://ldap.esup-portail.org&lt;/url&gt;<br>      &lt;/server&gt;<br>      &lt;server&gt;<br>        &lt;url&gt;ldap://replica.esup-portail.org&lt;/url&gt;<br>      &lt;/server&gt;<br>      &lt;disable_multiple_accounts/&gt;<br>    &lt;/config&gt;<br>  &lt;/handler&gt;<br>&lt;/authentication&gt;</pre>
<p>When using the bind method, the administrator should specify:</p>
<ul>
    <li>the <strong>start point of the search</strong> (e.g. dc=univ-rennes1,dc=fr). Tokens described in the filter attribute (see above) can be used for substitution;
    <li>the <strong>search scope</strong> (can be sub, one or base). Defaults to sub;
    <li>the <strong>filter</strong> to find the users' DN in the directory;
    <li><strong>parameters to connect to the LDAP directory</strong> (when ommited, an anonymous connection is used);
    <li>whether <strong>multiple accounts for users</strong> are allowed or not, thanks to the &lt;enable_multiple_accounts&gt; and &lt;disable_multiple_accounts&gt; tags (by default, they are not allowed). If the result of the LDAP search has more then one result, no connection to the LDAP directory is done and the authentication is refused. This feature was added in version 2.0.5. </li>
</ul>
<h2>LDAP servers</h2>
<p>The LDAP servers are defined by:</p>
<ul>
    <li>their LDAP <strong>URL</strong>. </li>
</ul>
<p>When specifying several servers, all the servers are considered as replicates: when authentication fails on one directory, LDAP authentication fails because directories are intended to contain the same data; next (redundant) servers are tried only if the first one does not respond.</p>
<p>Specifying many replicas can be done by specifying a space-separated list of LDAP URLs in the &lt;url&gt; element (these URLs will be handled by JNDI (since JDK 1.4) as precised in <a href="http://java.sun.com/products/jndi/tutorial/ldap/misc/url.html"><u><font color=#0000ff>http://java.sun.com/products/jndi/tutorial/ldap/misc/url.html</font></u></a>. Alternatively, using several &lt;server&gt; elements can help debugging. </p>
<!-- -->
<img src ="http://www.blogjava.net/junky/aggbug/122165.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-06-05 16:35 <a href="http://www.blogjava.net/junky/archive/2007/06/05/122165.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>cas实现sso注意的2个问题(转)</title><link>http://www.blogjava.net/junky/archive/2007/05/31/121098.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Thu, 31 May 2007 03:07:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/31/121098.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/121098.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/31/121098.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/121098.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/121098.html</trackback:ping><description><![CDATA[<div>
<p><font size=4>弄了一下午的cas安装终于成功了，，现在将步骤叙述一下<br>下载</font> <span><span><font size=4>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br><strong>cas-server-3.0.4.zip&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </strong></font><a href="http://www.ja-sig.org/downloads/cas/cas-server-3.0.4.zip"><strong><font size=4>http://www.ja-sig.org/downloads/cas/cas-server-3.0.4.zip</font> </strong></a></span></span><span><br></span><span><br><span><strong><font size=4>cas-client-2.0.11.zip&nbsp; </font></strong></span><a href="http://www.ja-sig.org/downloads/cas-clients/cas-client-2.0.11.zip"><strong><font size=4>http://www.ja-sig.org/downloads/cas-clients/cas-client-2.0.11.zip</font> </strong></a></span></p>
<p><font size=4>环境为 winxp&nbsp; tomcat5.5.17&nbsp; jdk1.5.0_05<br>1. 生成服务端证书<br>keytool -genkey -alias casserver -keyalg RSA -keystore tomcat.jks<br>CN：localhost<br>其他的随便&nbsp;&nbsp; <br>这里要注意姓名必须是服务器的域名 由于我是在本机所以是localhost <br>如果不这样写就会报如下异常：<br>java.io.IOException: HTTPS hostname wrong:&nbsp; should be &lt;localhost&gt;<br><br>这个异常出现在创建证书的第一个问题 姓名。其实这个&#8220;姓名&#8221;应该是域名。比如说localhost或是blogjava.net之类的。输成了姓名，和真正运行的时候域名不符，就会出错。浏览器无所谓，弹出一个对话框，用户按一下就行了。但http client就直接抛出了异常。--google出来的<br><br>2. 导出证书<br>keytool -export -file cas.cer -alias&nbsp;tomcat -keystore tomcat.jks<br><br>3.把导出的证书导入到jre/lib/security的cacerts中<br>这里又有一点要注意的&nbsp;&nbsp; 可能由于我的jre在线升级过一次 现在的工作的jre目录在C:\Program Files\Java\jre1.5.0_06\ 所以要把证书导入到C:\Program Files\Java\jre1.5.0_06\lib\security的cacerts中去<br>我一开始就是因为 导入到了%JAVA_HOME%的jre中 结果弄了半天都不成功报异常如下：<br>javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target<br><br>4.配置tomcat使用ssl<br>&lt;Connector&nbsp;port="8443" maxHttpHeaderSize="8192"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; maxThreads="150" minSpareThreads="25" maxSpareThreads="75"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; enableLookups="false" disableUploadTimeout="true"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; acceptCount="100" scheme="https" secure="true"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; clientAuth="false"//单向SSL<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; keystoreFile="C:\tomcat.jks" //我生成的keystore存放在c盘<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sslProtocol="TLS" /&gt;<br><br>5.</font> <font size=4><font face=宋体>部署CAS应用<br>&nbsp;</font> <span>解压</span> <span>cas-server-<st1:chsdate w:st="on" isrocdate="False" islunardate="False" day="30" month="12" year="1899">3.0.4</st1:chsdate>.zip,</span> <span>提取</span> <span>\target\cas.war </span><span>文件，将</span> <span>cas.war</span> <span>文件复制到</span> <span>TOMCAT</span> <span>的安装路径</span> <span>webapps</span> <span>下，启动</span> <span>TOMCAT</span> <span>，检查</span> <span>CAS</span> </font><font size=4><span>应用是否正常启动。<br></span><br>6.客户端配置<br>下载<span><font face=宋体>cas-client-2.0.11.zip</font></span></font> <span><br><font size=4>把</font> <span><font size=4>java/lib/casclient.jar考到 你所发布的webapp的WEB-INF/lib下<br>修改WEB-INF/web.xml 在里面添加过滤器<br>&nbsp;&nbsp;&nbsp; &lt;filter&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-name&gt;CASFilter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-class&gt;edu.yale.its.tp.cas.client.filter.CASFilter&lt;/filter-class&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;edu.yale.its.tp.cas.client.filter.loginUrl&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;https://localhost:8443/cas/login&lt;/param-value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;edu.yale.its.tp.cas.client.filter.validateUrl&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;https://localhost:8443/cas/proxyValidate&lt;/param-value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;edu.yale.its.tp.cas.client.filter.serverName&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;localhost:8443&lt;/param-value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/filter&gt;<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &lt;filter-mapping&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-name&gt;CASFilter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/filter-mapping&gt;<br><br>把里面的localhost换成域名<br>8443换成你所使用的端口</font> </span></span></p>
<p><span><span><font size=4>接着连接 </font><a href="https://localhost:8443/app1"><font size=4>https://localhost:8443/app1</font> </a><font size=4>&nbsp;&nbsp; //app1是你要实现sso的web应用<br>接着页面就会跳转到cas的登陆页面 就像你在第5步看到的页面一样 <br>随便用一个用户名登陆 只要 用户名和密码一样就可以登陆<br>可以在app2上重复步骤6 这样只要在app1上登陆后 再连接app2的url就不用再次登陆了<br>这就是单点登陆了。</font> </span><font size=4></font></span></p>
<p><font size=4><span><span>&nbsp;其实是一个很简单的事情 但是过程中要注意 步骤1和3 </span></span><span></span></font></p>
</div>
<img src ="http://www.blogjava.net/junky/aggbug/121098.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-31 11:07 <a href="http://www.blogjava.net/junky/archive/2007/05/31/121098.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>liferay+cas sso 配置</title><link>http://www.blogjava.net/junky/archive/2007/05/28/120393.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Mon, 28 May 2007 01:56:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/28/120393.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/120393.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/28/120393.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/120393.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/120393.html</trackback:ping><description><![CDATA[拷贝以下代码到liferay的web.xml文件：<br><br>&lt;filter&gt;<br>&nbsp;&lt;filter-name&gt;CAS Required&lt;/filter-name&gt;<br>&nbsp;&lt;filter-class&gt;edu.yale.its.tp.cas.client.filter.CASFilter&lt;/filter-class&gt;<br>&nbsp;&nbsp;&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;edu.yale.its.tp.cas.client.filter.loginUrl&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;https://localhost:8443/cas/login&lt;/param-value&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;<br>&nbsp;&nbsp;&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;edu.yale.its.tp.cas.client.filter.validateUrl&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;https://localhost:8443/cas/serviceValidate&lt;/param-value&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;<br>&nbsp;&nbsp;&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;edu.yale.its.tp.cas.client.filter.serverName&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;localhost:8081&lt;/param-value&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;<br>&nbsp;&lt;/filter&gt;<br><br>&lt;filter-mapping&gt;<br>&lt;filter-name&gt;CAS Required&lt;/filter-name&gt;<br>&lt;url-pattern&gt;/c/portal/layout&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&lt;filter-name&gt;CAS Required&lt;/filter-name&gt;<br>&lt;url-pattern&gt;/c/portal/login&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&lt;filter-name&gt;CAS Required&lt;/filter-name&gt;<br>&lt;url-pattern&gt;/c/portal/group_forward&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br>&lt;filter-mapping&gt;<br>&lt;filter-name&gt;CAS Required&lt;/filter-name&gt;<br>&lt;url-pattern&gt;/c/portal/remove_portlet&lt;/url-pattern&gt;<br>&lt;/filter-mapping&gt;<br><br>将以下代码拷贝到portal-ext.properties：<br><br>company.security.auth.type=userId<br>auto.login.hooks=com.liferay.portal.security.auth.BasicAutoLogin,com.liferay.portal.security.auth.CASAutoLogin<br># disable auth pipeline (we use CAS for&nbsp;all this via auto.login.hooks)<br>auth.pipeline.pre=<br>auth.pipeline.enable.liferay.check=false<br><br>修改cas client的代码：edu.yale.its.tp.cas.client.Util<br>在函数getService中找到<br>if (request.getQueryString() != null)<br>修改为 <br>if (request.getQueryString() != null &amp;&amp; !"".equals(request.getQueryString().trim()))<br>编译，打包到liferay/WEB-INF/lib目录中。<br>
<img src ="http://www.blogjava.net/junky/aggbug/120393.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-28 09:56 <a href="http://www.blogjava.net/junky/archive/2007/05/28/120393.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Liferay　SSO 集成两说</title><link>http://www.blogjava.net/junky/archive/2007/05/28/120391.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Mon, 28 May 2007 01:55:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/28/120391.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/120391.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/28/120391.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/120391.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/120391.html</trackback:ping><description><![CDATA[<div class=cnt>
<p>在liferay portal中集成CAS非常容易<br>1拷贝cas java client 的jar包到liferay\WEB-INF\lib下<br>2修改common\lib\ext\portal-ejb.jar\里的文件system.properties<br>&nbsp;&nbsp;&nbsp;&nbsp; 将poratl.properties里<br>auto.login.hooks=com.liferay.portal.auth.BasicAutoLogin,com.liferay.portal.auth.CASAutoLogin<br>3 修改liferay\WEB-INF里的web.xml文件增加<br>参数要根据实际情况修改<br>&lt;filter&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-name&gt;CASFilter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-class&gt;edu.yale.its.tp.cas.client.filter.CASFilter&lt;/filter-class&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;edu.yale.its.tp.cas.client.filter.loginUrl&lt;/param-name&gt;<br>&lt;param-value&gt;https://192.168.210.79:7443/CASServer/login&lt;/param-value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp; &lt;param-name&gt;edu.yale.its.tp.cas.client.filter.validateUrl&lt;/param-name&gt;<br>&lt;param-value&gt;https://192.168.210.79:7443/CASServer/serviceValidate&lt;/param-value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;<br>&nbsp;&nbsp;&nbsp; &lt;init-param&gt;<br>&nbsp; &lt;param-name&gt;edu.yale.its.tp.cas.client.filter.serverName&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp; &lt;param-value&gt;localhost&lt;/param-value&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;<br>&nbsp;&nbsp; &lt;/filter&gt;<br>&nbsp;&nbsp;&nbsp; &lt;filter-mapping&gt;<br>&nbsp;&nbsp;&nbsp; &lt;filter-name&gt;CASFilter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&nbsp;&nbsp; &lt;/filter-mapping&gt;</p>
<p><br>&nbsp;&nbsp; CAS安装- -<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>看了网上很多CAS安装的步骤,结合自己的经验总结如下:</p>
<p><br>一、配置Tomcat，启用SSL协议。</p>
<p><br>1.在CAS要安装的机器上（也就是服务端）为Tomcat生成用于SSL通讯的密钥：keytool -genkey -alias tomcat -keyalg RSA，输入密钥密码和相应参数，（注意：第一个参数CN一定要输入CAS安装机器名，其他参数就随便了），结果是在用户目录中创建了名为.keystore的密钥文件。</p>
<p><br>2.从服务端导出密钥文件：keytool -export -file server.crt -alias tomcat，输入上一步中的密码，结果在当前目录生成server.crt密钥文件。（注意：这个文件是要导入客户端的JVM上的）</p>
<p><br>3.为客户端的JVM导入密钥：keytool -import -keystore $JAVA_HOME/jre/lib/security/cacerts -file server.crt -alias tomcat，输入密码（注意：这里的密码不是上面设定的密码，而是changeit），将创建cacerts文件。</p>
<p><br>4.修改服务端的Tomcat配置文件server.xml，去掉对于SSL的注释，即开放8443端口，注意这里需要在connector字段中加入keystorePass="password"参数，password即为上面几步中涉及到的密码，keystoreFile＝".keystorePath",.keystorePath即为在第一步中生成的文件.keystore的全路径，如/usr/java/bin/.keystore。</p>
<p><br>5.启动Tomcat，测试<a href="https://server:8443/"><u><font color=#0000ff>https://server:8443/</font></u></a>是否是需经过验证方可访问（注意：server为服务端的IP地址或机器名）。</p>
<p>二、部署CAS Server 2.0.12到Tomcat</p>
<p><br>1.一种简单的方法是将下载包中的cas.war文件直接复制到Tomcat的webapps目录下。</p>
<p><br>2.另外一种方法，从sourceforge上找到ESUP-Portail CAS Generic Handler项目，利用esup-cas-quick-start生成一个最简的TOMCAT，详见我的下一篇文章。</p>
<p><br>3.启动Tomcat，测试<a href="https://server:8443/cas"><u><font color=#0000ff>https://server:8443/cas</font></u></a>，是否可访问CAS主页面（注意：server为服务端的IP地址或机器名）。</p>
<p>三、部署CAS Client 2.0.11到Servlet-Examples</p>
<p><br>1.利用Servlet-Examples实例进行测试，将下载包中的casclient.jar文件复制到Servlet-Examples中WEB-INF目录的lib下，这里需要手工建立lib目录。</p>
<p><br>2.修改Servlet-Examples的配置文件web.xml，加入以下的过滤器：</p>
<p>&lt;!-- CAS Filters --&gt;<br>&nbsp;&nbsp;&nbsp; &lt;filter&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-name&gt;CASFilter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-class&gt;edu.yale.its.tp.cas.client.filter.CASFilter&lt;/filter-class&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;edu.yale.its.tp.cas.client.filter.loginUrl&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;https://server:8443/cas/login&lt;/param-value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;&lt;!--这里的server是服务端的IP--&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;edu.yale.its.tp.cas.client.filter.validateUrl&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;https://serName:8443/cas/proxyValidate&lt;/param-value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;&lt;!--这里的serName是服务端的主机名，而且必须是--&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;edu.yale.its.tp.cas.client.filter.serverName&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;client:port&lt;/param-value&gt;&lt;!--client:port就是需要CAS需要拦截的地址和端口，一般就是这个TOMCAT所启动的IP和port--&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/filter&gt;<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; &lt;filter-mapping&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-name&gt;CASFilter&lt;/filter-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/filter-mapping&gt;</p>
<p>四、测试</p>
<p><br>1.启动Tomcat，定位到Servlet-Examples应用，点击Execute；</p>
<p><br>2.浏览器跳转至CAS登录首页，注意在URL中已经附上了Servlet-Examples的Service名</p>
<p>3.输入用户名和密码，这里没有对其验证条件做修改，因此只要用户名和密码相同即可通过验证。</p>
<p><br>4.验证通过后浏览器又重新定位至Servlet-Examples并显示该Servlet的内容。</p>
<p><br>5.点击Http Header的Servlet应用，可以看到里面对当前用户的用户名信息做了记录。</p>
<p>6.以后访问Servlet-Examples应用都无需再次输入用户名和密码了。</p>
<p>至此，CAS Server和Client已经在Tomcat上成功部署与配置，并达到了预期的SSO效果。<br></p>
</div>
<img src ="http://www.blogjava.net/junky/aggbug/120391.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-28 09:55 <a href="http://www.blogjava.net/junky/archive/2007/05/28/120391.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java keytool工具的作用及使用方法</title><link>http://www.blogjava.net/junky/archive/2007/05/22/119119.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Tue, 22 May 2007 06:00:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/22/119119.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/119119.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/22/119119.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/119119.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/119119.html</trackback:ping><description><![CDATA[Keytool 是安全钥匙与证书的管理工具.它管理一个存储了私有钥匙和验证相应公共钥匙的与它们相关联的X.509 证书链的keystore(相当一个数据库).<br><br>Keytool 是一个有效的安全钥匙和证书的管理工具. 它能够使用户使用数字签名来管理他们自己的私有/公共钥匙对,管理用来作自我鉴定的相关的证书,管理数据完整性和鉴定服务.它还能使用户在通信时缓存它们的公共钥匙.<br><br>一个证书是某一实体(个人,公司等)的数字签名,指出其他实体的公共钥匙(或其他信息)的详细的值.当数据被签名后,这个签名信息被用来检验数据的完整性和真实性.完整性指数据没有被修改和篡改,真实性指数据从任何产生和签名的一方真正的传输到达.<br><br>Keytool 把钥匙和证书储存到一个keystore.默任的实现keystore的是一个文件.它用一个密码保护钥匙.<br><br>而另外的一个工具jarsigner用keystore中的信息产生或检验Java aRchive(jar文件)中的数字签名.<br><br>Keystore有两个不同的入口:<br>1.钥匙入口:保存了非常敏感的加密的钥匙信息,并且是用一个保护的格式存储以防止未被授权的访问.以这种形式存储的钥匙是秘密钥匙,或是一个对应证书链中公有钥匙的私有钥匙.<br>2.信任证书入口:包含一个属于其他部分的单一公共钥匙证书.它之所以被称为"信任证书",是因为keystore信任的证书中的公共钥匙真正属于证书所有者的身份识别.<br><br>Keystore的别名:<br>所有的keystore入口(钥匙和信任证书入口)是通过唯一的别名访问.别名是 不区分大小写的.如别名Hugo和hugo指向同一个keystore入口.<br>可以在加一个入口到keystore的时候使用-genkey参数来产生一个钥匙对(公共钥匙和私有钥匙)时指定别名.也可以用-import参数加一个证书或证书链到信任证书.<br>如:<br>keytool -genkey -alias duke -keypass dukekeypasswd<br>其中duke为别名,dukekeypasswd为duke别名的密码.这行命令的作用是产生一个新的公共/私有钥匙对.<br><br>假如你想修改密码,可以用:<br>keytool -keypasswd -alias duke -keypass dukekeypasswd -new newpass<br>将旧密码dukekeypasswd改为newpass.<br><br>Keystore的产生:<br>1.当使用-genkey 或-import或-identitydb命令添加数据到一个keystore,而当这个keystore不存在时,产生一个keystore.默认名是.keystore,存放到user-home目录.<br>2.当用-keystore指定时,将产生指定的keystore.<br><br>Keystore的实现:<br>Keytool 类位于java.security包下,提供一个非常好的接口去取得和修改一个keystore中的信息. 目前有两个命令行:keytool和jarsinger,一个GUI工具Policy 可以实现keystore.由于keystore是公开的,用户可以用它写一些额外的安全应用程序.<br>Keystore还有一个sun公司提供的內在实现.它把keystore作为一个文件来实现.利用了一个keystore类型(格式)"JKS".它用单独的密码保护每一个私有钥匙.也用可能不同的密码保护整个keystore的完整性.<br><br>支持的算法和钥匙大小:<br>keytool允许用户指定钥匙对和注册密码服务供应者所提供的签名算法.缺省的钥匙对产生算法是"DSA".假如私有钥匙是"DSA"类型,缺省签名算法是"SHA1withDSA",假如私有钥匙是"RSA"类型,缺省算法是"MD5withRSA".<br>当产生一个DSA钥匙对,钥匙必须在512-1024位之间.对任何算法的缺省钥匙大小是1024位.<br><br>证书:<br>一个证书是一个实体的数字签名,指出其他实体的公共钥匙有明确的值.<br>1.公共钥匙 :是同一个详细的实体的数字关联,并有意让所有想同这个实体发生信任关系的其他实体知道.公共钥匙用来检验签名;<br>2.数字签名:假如数据已被签名,并用身份存储在一个实体中,一个签名能够证明这个实体知道这个数据.这个数据用实体私有钥匙签名并递交;<br>3.身份:知道实体的方法.在一些系统中身份是公共钥匙,其他系统中可以是从一个X.509名字的邮件地址的Unix UID来的任何东西;<br>4.签名:一个签名用用实体私有钥匙来计算某些加密数据;<br>5.私有钥匙:是一些数字,每一个私有钥匙只能被特定的拥有该私有钥匙的实体知道.私有和公共钥匙存在所有用公共钥匙加密的系统的钥匙对中.一个公共钥匙加密(如DSA),一个私有钥匙与一个正确的公共钥匙通信.私有钥匙用来计算签名.<br>6.实体:一个实体可以是一个人,一个组织,一个程序,一台计算机,一个商业,一个银行,或其他你想信任的东西.<br><br>Keytool应用实例:<br><br>1.产生一个keystore:<br>keytool -genkey -alias User(keystore的别名) -keyalg RSA -validity 7 -keystore keystore(指定keystore).<br>运行这个命令,系统提示:<br>Enter keystore password:yourpassword(输入密码)<br>What is your first and last name?<br>[Unknown]: your name(输入你的名字)<br>What is the name of your organizational unit?<br>[Unknown]:your organizational(输入你所在组织单位的名字)<br>What is the name of your organization?<br>[Unknown]:your organization name (输入你所在组织的名字)<br>What is the name of your City or Locality?<br>[Unknown]:your city name(输入所在城市的名字)<br>What is the name of your State or Province?<br>[Unknown]:your provice name(输入所在省份名字)<br>What is the two-letter country code for this unit?<br>[Unknown]:cn(输入国家名字)<br>Is CN=your name, OU=your organizaion, O="your organization name",<br>L=your city name, ST=your province name, C=cn correct?<br>[no]: yes<br><br>2.检查一个keystore:<br>keytool -list -v -keystore keystore<br>Enter keystore password:your password(输入密码)<br>将显示keystore內容如:<br>Keystore type: jks<br>Keystore provider: SUN<br>Your keystore contains 1 entry<br>Alias name: yourname<br>Creation date: Dec 20, 2001<br>Entry type: keyEntry<br>Certificate chain length: 1<br>Certificate[1]:<br>Owner: CN=yourname, OU=your organization, O="your organization name",<br>L=your city name, ST=your province name, C=CN<br>Issuer: CN=Duke, OU=Java Software, O="Sun Microsystems, Inc.", L=Palo Alto, ST=CA, C=US<br>Serial number: 3c22adc1<br>Valid from: Thu Dec 20 19:34:25 PST 2001 until: Thu Dec 27 19:34:25 PST 2001<br>Certificate fingerprints:<br>MD5: F1:5B:9B:A1:F7:16:CF:25:CF:F4:FF:35:3F:4C:9C:F0<br>SHA1: B2:00:50:DD:B6:CC:35:66:21:45:0F:96:AA:AF:6A:3D:E4:03:7C:74<br><br>3.输出keystore到一个文件:testkey:<br>keytool -export -alias duke -keystore keystore -rfc -file testkey<br>系统输出:<br>Enter keystore password:your password(输入密码)<br>Certificate stored in file<br><br>4.输入证书到一个新的truststore:<br>keytool -import -alias dukecert -file testkey -keystore truststore<br>Enter keystore password:your new password.(输入truststore新密码)<br><br>5.检查truststore:<br>keytool -list -v -keystore truststore<br>系统将显示truststore的信息.<br>现在可以用适当的keystore运行你的应用程序.如:<br>java -Djavax.net.ssl.keyStore=keystore -Djavax.net.ssl.keyStorePassword=password Server<br>和: java -Djavax.net.ssl.trustStore=truststore<br>-Djavax.net.ssl.trustStorePassword=trustword Client<br>
<img src ="http://www.blogjava.net/junky/aggbug/119119.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-22 14:00 <a href="http://www.blogjava.net/junky/archive/2007/05/22/119119.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>liferay portal 4.2.1 配置sso+cas </title><link>http://www.blogjava.net/junky/archive/2007/05/22/119112.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Tue, 22 May 2007 05:48:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/22/119112.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/119112.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/22/119112.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/119112.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/119112.html</trackback:ping><description><![CDATA[<p>按照官方文档配置：</p>
<p>Introduction <br>The following are a set of instructions for integrating Liferay Portal with CAS Server to setup single sign on (SSO) between Liferay and an existing web application. </p>
<p>[edit]Setting up CAS server <br>We will begin with setting up JA-SIG CAS server on Tomcat 5.x.x. </p>
<p>Download cas-server WAR from Liferay's download page or the whole distribution from here and drop the cas-web.war file into Tomcat's webapps dir. In a production environment The CAS server should really run on its own tomcat instance but for testing purposes we'll drop it in the same instance as our Liferay portal. </p>
<p>We'll need to edit the server.xml file in tomcat and uncomment the SSL section to open up port 8443. </p>
<p><connector maxhttpheadersize="8192" port="8443"></connector></p>
<p>xml 代码</p>
<p>&nbsp;</p>
<div>
<ol>
    <li>
    <p>&lt;Connector&nbsp;port="8443"&nbsp;maxHttpHeaderSize="8192"&nbsp;&nbsp; </p>
    <li>
    <p>maxThreads="150"&nbsp;minSpareThreads="25"&nbsp;maxSpareThreads="75"&nbsp;&nbsp; </p>
    <li>
    <p>enableLookups="false"&nbsp;disableUploadTimeout="true"&nbsp;&nbsp; </p>
    <li>
    <p>acceptCount="100"&nbsp;scheme="https"&nbsp;secure="true"&nbsp;&nbsp; </p>
    <li>
    <p>clientAuth="false"&nbsp;sslProtocol="TLS"&nbsp;/&gt;&nbsp;&nbsp; </p>
    </li>
</ol>
</div>
<p><br>[edit]Setting up the CAS client <br>Next we need to download the Yale CAS client from here. Get cas-client-2.0.11. Place the casclient.jar in ROOT/web-inf/lib of the Liferay install. </p>
<p>[edit]Generate the SSL cert with Java keytool <br>Now that we have everything we need, it's time to generate an SSL cert for our CAS server. Instructions and more information on SSL certs can be found here(http://www.ja-sig.org/products/cas/downloads/index.html) </p>
<p>（我下载的就是这个版本，3.0应该也是可以的，我没有测试。）</p>
<p>But I found some typos and errors on that page. So following the instructions below should get you what you need. </p>
<p>In any directory ( I use my root ) enter the command: </p>
<p>keytool -genkey -alias tomcat -keypass changeit -keyalg RSA </p>
<p>Answer the questions: (note that your firstname and lastname MUST be hostname of your server and cannot be a IP address; this is very important as an IP address will fail client hostname verification even if it is correct) </p>
<p>Enter keystore password:&nbsp; changeit<br>What is your first and last name?<br>[Unknown]:&nbsp; localhost<br>What is the name of your organizational unit?<br>[Unknown]:<br>What is the name of your organization?<br>[Unknown]:<br>What is the name of your City or Locality?<br>[Unknown]:<br>What is the name of your State or Province?<br>[Unknown]:<br>What is the two-letter country code for this unit?<br>[Unknown]:<br>Is CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct?<br>[no]: yes</p>
<p>（这点需要注意，都需要添上，否则不能通过，不会生成.keystore目录；下面的文件名随便输入一个）<br>Then enter the command: </p>
<p>keytool -export -alias tomcat -keypass changeit -file %FILE_NAME% <br>I use server.cert for %FILE_NAME%. This command exports the cert you generated from your personal keystore (In windows your personal keystore is in C:\Documents and Settings\<username></username>\.keystore) </p>
<p>Finally import the cert into Java's keystore with this command. Tomcat uses the keystore in your JRE (%JAVA_HOME%/jre/lib/security/cacerts) </p>
<p>keytool -import -alias tomcat -file %FILE_NAME% -keypass changeit -keystore %JAVA_HOME%/jre/lib/security/cacerts<br>Startup the CAS server </p>
<p>Now you are ready to startup your CAS server. Simply startup Tomcat and access CAS with https://localhost:8443/cas You should see the CAS login screen and no errors in your catalina logs. </p>
<p>[edit]Setting up Liferay Portal <br>[edit]web.xml <br>Note: If you are using Liferay 4.2, this filter is already defined. All you have to do is modify the URL parameters, if your CAS server is at a different location. </p>
<p>It's time to move on to configuring Liferay. In the web.xml file you will need to add a new filter and its mapping directly above the first existing auto login filter mapping. This new filter we just added will redirect all login attempts to the CAS server. If your hostname is different you can modify the init-params accordingly.&nbsp;<br><filter-name></filter-name>&nbsp;</p>
<p>xml 代码</p>
<p>&nbsp;</p>
<div>
<ol>
    <li>
    <p>&lt;filter&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;filter-name&gt;CAS&nbsp;Filter&lt;/filter-name&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;filter-class&gt;edu.yale.its.tp.cas.client.filter.CASFilter&lt;/filter-class&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;init-param&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-name&gt;edu.yale.its.tp.cas.client.filter.loginUrl&lt;/param-name&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-value&gt;https://localhost:8443/cas-web/login&lt;/param-value&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;/init-param&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;init-param&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-name&gt;edu.yale.its.tp.cas.client.filter.validateUrl&lt;/param-name&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-value&gt;https://localhost:8443/cas-web/proxyValidate&lt;/param-value&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;/init-param&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;init-param&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-name&gt;edu.yale.its.tp.cas.client.filter.serviceUrl&lt;/param-name&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-value&gt;http://localhost:8080/c/portal/login&lt;/param-value&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;/init-param&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&lt;/filter&gt;&nbsp;&nbsp; </p>
    </li>
</ol>
</div>
<p><br><br>If you use a ...serviceUrl param like above, after logging in with CAS, the browser will be redirected back to that serviceUrl. However, you can change it to the following and it will redirect back to the full URL that was originally requested. This allows you to have a deep link (e.g. to a certain layout with parameters for a portlet even) that is preserved through the CAS login process: </p>
<p>xml 代码</p>
<p>&nbsp;</p>
<div>
<ol>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;init-param&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-name&gt;edu.yale.its.tp.cas.client.filter.serverName&lt;/param-name&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-value&gt;localhost:8080&lt;/param-value&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;/init-param&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp; </p>
    <li>
    <p>&lt;filter-mapping&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;filter-name&gt;CAS&nbsp;Filter&lt;/filter-name&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;url-pattern&gt;/c/portal/login&lt;/url-pattern&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&lt;/filter-mapping&gt;&nbsp;&nbsp; </p>
    </li>
</ol>
</div>
<p>Then add the following to the rest of the auto login filters <filter-mapping></filter-mapping></p>
<p>xml 代码</p>
<p>&nbsp;</p>
<div>
<ol>
    <li>
    <p>&lt;filter-mapping&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;filter-name&gt;Auto&nbsp;Login&nbsp;Filter&lt;/filter-name&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;url-pattern&gt;/c/portal/login&lt;/url-pattern&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;dispatcher&gt;INCLUDE&lt;/dispatcher&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&nbsp;&nbsp;&nbsp;&lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;&nbsp; &nbsp;&nbsp; </p>
    <li>
    <p>&lt;/filter-mapping&gt;&nbsp;&nbsp; </p>
    </li>
</ol>
</div>
<p><br><br>[edit]system-ext.properties <br>Note: this is only needed in Liferay 4.2 </p>
<p>Set the com.liferay.filters.sso.cas.CASFilter setting to true. </p>
<p>(&nbsp;system-ext.properties这个文件不存在，新建一个\ROOT\WEB-INF\classes\system-ext.properties，然后填入该内容)</p>
<p>Place the following in system-ext.properties: </p>
<p>&nbsp;&nbsp; #<br>&nbsp;&nbsp; # The CAS filter will redirect the user to the CAS login page for SSO. See<br>&nbsp;&nbsp; # http://www.ja-sig.org/products/cas for more information.<br>&nbsp;&nbsp; #<br>&nbsp;&nbsp; com.liferay.filters.sso.cas.CASFilter=true</p>
<p>（portal-ext.properties 该文件存在，仅仅添加内容即可）<br>[edit]portal-ext.properties <br>Put this in portal-ext.properties. </p>
<p>##<br>## Auto Login<br>##<br>#<br># Input a list of comma delimited class names that implement<br># com.liferay.portal.security.auth.AutoLogin. These classes will run in<br># consecutive order for all unauthenticated users until one of them return a<br># valid user id and password combination. If no valid combination is<br># returned, then the request continues to process normally. If a valid<br># combination is returned, then the portal will automatically login that<br># user with the returned user id and password combination.<br>#<br># For example, com.liferay.portal.security.auth.BasicAutoLogin reads from a<br># cookie to automatically log in a user who previously logged in while<br># checking on the "Remember Me" box.<br>#<br># This interface allows deployers to easily configure the portal to work<br># with other SSO servers. See com.liferay.portal.security.auth.CASAutoLogin<br># for an example of how to configure the portal with Yale's SSO server.<br>#<br>#auto.login.hooks=com.liferay.portal.security.auth.BasicAutoLogin<br>auto.login.hooks=com.liferay.portal.security.auth.BasicAutoLogin,com.liferay.portal.security.auth.CASAutoLogin<br>Comment the first auto.login.hooks property and uncomment the second to add CASAutoLogin to the list of AutoLogin implementations. </p>
<p>[edit]Startup Liferay and Test <br>Startup the portal and when the homepage loads up hit the login link. If all goes well you should be redirected to the CAS server's login screen. Login to CAS with liferay.com.1 as your username and liferay.com.1 as your password. You should now be logged into the portal. </p>
<p>The current auth scheme for CAS is quite simple but in production an auth scheme which taps into an LDAP repository or some other auth service will be required. </p>
<p>[edit]Troubleshooting <br>If you created a cert with the %FILE_NAME%, you'll probably run into problems. Here are 2 commands to delete the tomcat alias from the keystore so you can start fresh: </p>
<p>keytool -delete -alias tomcat -keystore %JAVA_HOME%/jre/lib/security/cacerts<br>keytool -delete -alias tomcat -file server.cert<br>You may not be able to get https://localhost:8443/cas up and running after the cert key generation. If so, skip the test and try it after you've finished all the steps. If you can't login at that point, you've probably generated your cert incorrectly. <br>I've had problems with certs on IE7, make sure you try it out on Firefox and Opera. <br>[edit]Lifecast <br>CAS Setup - Integrate Liferay Portal with a CAS server to access multiple applications with a single sign on. </p>
<p>Retrieved from "http://wiki.liferay.com/index.php/Single_SignOn_-_Integrating_Liferay_With_CAS_Server"<br>Category: Customization</p>
<img src ="http://www.blogjava.net/junky/aggbug/119112.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-22 13:48 <a href="http://www.blogjava.net/junky/archive/2007/05/22/119112.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CAS及客户端Acegi的安装配置指南（下）</title><link>http://www.blogjava.net/junky/archive/2007/05/22/119071.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Tue, 22 May 2007 03:10:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/22/119071.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/119071.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/22/119071.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/119071.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/119071.html</trackback:ping><description><![CDATA[<p>&nbsp;四．改造acegi-security-sample-tutorial</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 解压缩acegi-security-1.0.1.zip，拷贝acegi-security-sample-tutorial.war到%CATALINA_HOME%/webapps目录下，重启tomcat，acegi-security-sample-tutorial即已发布。现在我们将其改造为使用CAS进行用户的登录和认证。</p>
<p>&nbsp;&nbsp;&nbsp; 用编辑器打开WEB-INF/applicationContext-acegi-security.xml，找到</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationManager" ref="authenticationManager"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationFailureUrl" value="/acegilogin.jsp?login_error=1"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="defaultTargetUrl" value="/"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="filterProcessesUrl" value="/j_acegi_security_check"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="rememberMeServices" ref="rememberMeServices"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>将其替换为：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.cas.CasProcessingFilter"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationManager" ref="authenticationManager"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="authenticationFailureUrl" value="/acegilogin.jsp?login_error=1"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="defaultTargetUrl" value="/"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="filterProcessesUrl" value="/j_acegi_cas_security_check"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="rememberMeServices" ref="rememberMeServices"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>其中，authenticationFailureUrl是认证失败时显示的页面，acegi-security-sample-tutorial登录失败时会在登录页(acegilogin.jsp)显示失败原因，现改为使用CAS之后，acegi-security-sample-tutorial使用CAS的登录页面，故acegilogin.jsp可去掉。接下来，找到</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="loginFormUrl" value="/acegilogin.jsp"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="forceHttps" value="false"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>替换为：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean class="org.acegisecurity.ui.cas.CasProcessingFilterEntryPoint"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="loginUrl"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;https://localhost:8443/cas/login&lt;/value&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="serviceProperties"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="serviceProperties"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>再接下来，找到</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="providers"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;list&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref local="daoAuthenticationProvider"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="key" value="changeThis"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="key" value="changeThis"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/list&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>将&lt;ref local="daoAuthenticationProvider"/&gt;修改为&lt;ref local="casAuthenticationProvider"/&gt;，并添加以下bean：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="casAuthenticationProvider" class="org.acegisecurity.providers.cas.CasAuthenticationProvider"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="ticketValidator"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="ticketValidator"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="casProxyDecider"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="casProxyDecider"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="statelessTicketCache"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="statelessTicketCache"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="casAuthoritiesPopulator"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="casAuthritiesPopulator"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="key"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;some_unique_key&lt;/value&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="ticketValidator" class="org.acegisecurity.providers.cas.ticketvalidator.CasProxyTicketValidator"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="casValidate"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;https://localhost:8443/cas/proxyValidate&lt;/value&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="serviceProperties"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="serviceProperties"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="serviceProperties" class="org.acegisecurity.ui.cas.ServiceProperties"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="service"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;https://localhost:8443/acegi-security-sample-tutorial/j_acegi_cas_security_check&lt;/value&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="casProxyDecider" class="org.acegisecurity.providers.cas.proxy.RejectProxyTickets"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="statelessTicketCache" class="org.acegisecurity.providers.cas.cache.EhCacheBasedTicketCache"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="cache"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean class="org.springframework.cache.ehcache.EhCacheFactoryBean"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="cacheManager"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="cacheName" value="userCache"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="casAuthritiesPopulator" class="org.acegisecurity.providers.cas.populator.DaoCasAuthoritiesPopulator"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="userDetailsService"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="userDetailsService"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>改造完毕！</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; 五．配置CAS使用JDBC数据源进行用户认证</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CAS默认设置为只要用户名和密码相同，即可进行登录，这在现实使用中是不允许的。我们修改为使用MySQL的test数据库中的app_user表作为用户数据源。首先，我们在test库中创建一个表：</p>
<p>CREATE TABLE `app_user` (</p>
<p>&nbsp; `username` varchar(30) NOT NULL default '',</p>
<p>&nbsp; `password` varchar(45) NOT NULL default '',</p>
<p>&nbsp; PRIMARY KEY&nbsp; (`username`)</p>
<p>) ENGINE=InnoDB DEFAULT CHARSET=utf8;</p>
<p>并添加如下用户：</p>
<p>INSERT INTO `app_user` (`username`,`password`) VALUES </p>
<p>&nbsp;('dianne','emu'),</p>
<p>&nbsp;('marissa','koala'),</p>
<p>&nbsp;('peter','opal'),</p>
<p>&nbsp;('scott','wombat');</p>
<p>用编辑器打开%CATALINA_HOME%/webapps/cas/WEB-INF/deployerConfigContext.xml，找到</p>
<p>&nbsp;&nbsp;&nbsp; &lt;bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" /&gt;</p>
<p>注释掉该行，在其下加入：</p>
<p>&lt;bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="sql" value="select password from app_user where username=?" /&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="dataSource" ref="dataSource" /&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>并添加一个bean：</p>
<p>&nbsp;&nbsp;&nbsp; &lt;bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" destroy-method="close"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="driverClassName"&gt;&lt;value&gt;com.mysql.jdbc.Driver&lt;/value&gt;&lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="url"&gt;&lt;value&gt;jdbc:mysql://localhost:3306/test&lt;/value&gt;&lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="username"&gt;&lt;value&gt;test&lt;/value&gt;&lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="password"&gt;&lt;value&gt;test&lt;/value&gt;&lt;/property&gt;</p>
<p>&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</p>
<p>拷贝cas-server-jdbc-3.0.5-rc2.jar和mysql-connector-java-3.1.12-bin.jar到%CATALINA_HOME%/webapps/cas/WEB-INF/lib下。</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; 重新启动tomcat，在浏览器中输入<a href="http://localhost:8080/acegi-security-sample-tutorial">http://localhost:8080/acegi-security-sample-tutorial</a>，你会发现，一旦你访问了受保护的页面，请求就会被重定向到CAS的登录页面，登录成功之后请求会被再被定向到最初访问的页面，如果有多个系统，在这些系统之间进行切换将不会要求用户重新登录，这就达到了单点登录的目的。</p>
<p>&nbsp;</p>
<p>参考文献：</p>
<ul>
    <li>Acegi security官方网站及文档：<a href="http://acegisecurity.org/">http://acegisecurity.org</a>
    <li>CAS官方网站及文档：<a href="http://www.ja-sig.org/products/cas">http://www.ja-sig.org/products/cas</a>
    <li>SSL Configuration HOW-TO：<a href="http://tomcat.apache.org/tomcat-5.5-doc/ssl-howto.html">http://tomcat.apache.org/tomcat-5.5-doc/ssl-howto.html</a>
    <li>利用CAS实现SSO技术：<a href="http://kb.csdn.net/java/Articles/200606/2e082aae-775d-48be-b2fb-c2e218af9ba9.html">http://kb.csdn.net/java/Articles/200606/2e082aae-775d-48be-b2fb-c2e218af9ba9.html</a>
    <li>Spring in Action中文版（ISBN7-115-14315-3/TP.5173） </li>
</ul>
<p>&nbsp;</p>
<img src ="http://www.blogjava.net/junky/aggbug/119071.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-22 11:10 <a href="http://www.blogjava.net/junky/archive/2007/05/22/119071.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CAS及客户端Acegi的安装配置指南(上)</title><link>http://www.blogjava.net/junky/archive/2007/05/22/119070.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Tue, 22 May 2007 03:09:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/22/119070.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/119070.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/22/119070.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/119070.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/119070.html</trackback:ping><description><![CDATA[作者：龙智 (Dragon)<br>时间：2006-07-09<br><br>
<p style="TEXT-INDENT: 2em">CAS（Central Authentication Service）是耶鲁大学开发的一个开源的SSO（single sign on，单点登录）系统。它提供了丰富的客户端库，如Java, .NET, PHP, Perl等版本，使用这些库用户可以方便地给自己的应用程序加上CAS支持。Acegi security system for Spring是Spring的一个子项目，它为Java EE开发者提供了一个易于使用的提供认证和授权服务的安全框架。Acegi支持CAS，也可看作是CAS的一个Java版的Client。</p>
<p style="TEXT-INDENT: 2em">以下详细介绍如何配置CAS以及应用程序，使其利用Acegi和CAS进行用户的登录和认证。我将以acegi-security-1.0.1发布包中附带的acegi-security-sample-tutorial应用为例，它使用DaoAuthenticationProvider对用户进行认证，用户帐号和权限信息保存在一个properties文件中，我将对其进行改造，改造之后，acegi-security-sample-tutorial使用CAS进行用户认证，授权信息仍从该properties文件读取，因为CAS只负责认证，不负责授权，所以授权工作交由客户端Acegi来完成，CAS的用户源配置为数据库，利用JDBC进行读取。本文需要读者对SSO和Acegi有一定的了解。</p>
<p style="TEXT-INDENT: 2em">一．准备工作下载并安装Tomcat（&lt;a target="_blank" rel="nofollow" href="http://tomcat.apache.org/"&gt;http://tomcat.apache.org/&lt;/a&gt;），本文使用的版本是5.5.15； </p>
下载并安装MySQL（<a href="http://www.mysql.com/" target=_blank rel=nofollow><u><font color=#0000ff>http://www.mysql.com</font></u></a>），本文使用的版本是5.0.16； <br>下载CAS服务端（<a href="http://www.ja-sig.org/products/cas/" target=_blank rel=nofollow><u><font color=#800080>http://www.ja-sig.org/products/cas/</font></u></a>），本文使用最新的3.0.5RC2； <br>下载CAS-JDBC Adapter （<a href="http://developer.ja-sig.org/maven/cas/jars/cas-server-jdbc-3.0.5-rc2.jar" target=_blank rel=nofollow><u><font color=#800080>http://developer.ja-sig.org/maven/ca ... ver-jdbc-3.0.5-rc2.jar</font></u></a>）； <br>下载Acegi（<a href="http://acegisecurity.org/" target=_blank rel=nofollow><u><font color=#800080>http://acegisecurity.org/</font></u></a>）,本文使用的版本是1.0.1； <br>
<p style="TEXT-INDENT: 2em">二．安装CAS 解压缩cas-server-3.0.5-rc2.zip，拷贝target目录中的cas.war到%CATALINA_HOME%/webapps下即可。运行Tomcat，访问&lt;a target="_blank" rel="nofollow" href="http://localhost:8080/cas"&gt;http://localhost:8080/cas&lt;/a&gt;应可看到CAS登录界面。</p>
<p style="TEXT-INDENT: 2em"></p>
<p style="TEXT-INDENT: 2em">三．配置Tomcat支持SSL 由于CAS要求使用https和客户端进行通信，所以需要配置Tomcat支持SSL，首先介绍如何制作自签名证书以及将其导入到证书库。</p>
1. keytool -keystore keystore -alias acegisecurity -genkey -keyalg RSA -validity 9999 -storepass password -keypass password<br>What is your first and last name?<br>
<p style="TEXT-INDENT: 2em">[Unknown]: localhost</p>
其他随便填写即可。<br>2. keytool -export -v -rfc -alias acegisecurity -file acegisecurity.txt -keystore keystore -storepass password<br>3. copy acegisecurity.txt %JAVA_HOME%\jre\lib\security
<p style="TEXT-INDENT: 2em"></p>
4. copy keystore %CATALINA_HOME %<br>5. cd %JAVA_HOME%\jre\lib\security<br>6. keytool -import -v -file acegisecurity.txt -keypass password -keystore cacerts -storepass changeit -alias acegisecurity<br>
<p style="TEXT-INDENT: 2em">接下来，用编辑器打开%CATALINA_HOME%/conf/server.xml，找到</p>
<p style="TEXT-INDENT: 2em">&amp;lt;Connector port="8443" maxHttpHeaderSize="8192"</p>
<p style="TEXT-INDENT: 2em">maxThreads="150" minSpareThreads="25" maxSpareThreads="75"</p>
<p style="TEXT-INDENT: 2em">enableLookups="false" disableUploadTimeout="true"</p>
<p style="TEXT-INDENT: 2em">acceptCount="100" scheme="https" secure="true"</p>
<p style="TEXT-INDENT: 2em">clientAuth="false" sslProtocol="TLS" /&amp;gt;</p>
<p style="TEXT-INDENT: 2em">这一行默认是被注释掉的，取消注释，并加入keystoreFile="keystore" keystorePass="password"这两个属性，注意keystoreFile属性可以使用keystore文件的绝对路径，也可使用基于%CATALINA_HOME%环境变量的相对路径，keystorePass是访问keystore的密码，应和上面制作证书时设定的密码保持一致。</p>
访问<a href="https://localhost:8443/" target=_blank rel=nofollow><u><font color=#800080>https://localhost:8443</font></u></a>，应弹出一个对话框，告知用户正要访问的站点的证书不安全，是否接受，确认接受，应可看到那只熟悉可爱的小猫。
<img src ="http://www.blogjava.net/junky/aggbug/119070.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-22 11:09 <a href="http://www.blogjava.net/junky/archive/2007/05/22/119070.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>应用整合中SSO的技术实现</title><link>http://www.blogjava.net/junky/archive/2007/05/21/118909.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Mon, 21 May 2007 08:07:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/21/118909.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/118909.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/21/118909.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/118909.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/118909.html</trackback:ping><description><![CDATA[<p><font face=Verdana>在税务行业信息化发展的关键阶段，应用整合已经非常重要，而应用整合的表现层首先要实现的就是单点登陆（SSO，Single sign-on的缩写），以下是笔者结合南京地税进行应用整合中SSO的技术实现<!-- cio 正文页网画中画广告  -->
<table style="MARGIN: 10px 7px 3px 4px" cellSpacing=0 cellPadding=0 align=left border=0>
    <tbody>
        <tr>
            <td>
            <div id=ad_position_roller_38>&nbsp;</div>
            </td>
        </tr>
    </tbody>
</table>
。</font></p>
<p><font face=Verdana>南京地税目前有多种企业应用，包括征管系统、行政系统、辅助决策系统、公文系统、人事系统、电子地图、邮件系统等等，这些应用主要是采用三层体系结构（<a href="http://wiki.ccw.com.cn/IE" target=_blank><font color=#0000cc><strong><u>IE</u></strong></font></a>6.0+weblogic portal7.0 +weblogic server7.0 +<a href="http://topic.ccw.com.cn/corpCenter/407.html" target=_blank><font color=#0000cc><strong><u>oracle</u></strong></font></a>9i）来构架。所有的用户登陆需要通过weblogic portal进行。</font></p>
<p><font face=Verdana>我们在系统中使用SSO来实现用户通过一次认证（authenti<a href="http://topic.ccw.com.cn/corpCenter/249.html" target=_blank><font color=#0000cc><strong><u>ca</u></strong></font></a>te）来存取多个受保护的资源，也就是说，用户随后对受保护资源的存取，将不会导致每一次都要用户提供认证的信息，只需要一次认证即可。</font></p>
<p><font face=Verdana>一、SSO实现原理</font></p>
<p><font face=Verdana>1、概念：SSO的一种偏向技术的说法：用户只需登陆一次，就可使用多个SSO enable的应用系统。</font></p>
<p><font face=Verdana>（1）、单一的登陆点。理想的情况是用户通过任何应用系统都能进行SSO，这对于基于Web的系统是可行的。这种单一的登陆点在整个系统的设计中是唯一认证用户的地方，由登陆点将SSO token（针对不同的C/S，B/S应用可能还需要传递用户名，口令）传递给应用系统，应用系统利用SSO token来进行用户已认证的验证。我们将这个单一的登陆点称为SSO Entry。</font></p>
<p><font face=Verdana>（2）、SSO enable意味着对应用系统的修改不可避免。并不是任何系统都能够使用SSO，只有那些符合SSO规范，使用SSO <a href="http://wiki.ccw.com.cn/AP" target=_blank><font color=#0000cc><strong><u>AP</u></strong></font></a>I的应用系统才具有SSO的功能。简单地说就是要修改已有的应用系统，屏蔽已有的应用系统的用户认证模块，使用系统提供的SSO API来验证用户，以及对用户的操作进行授权。</font></p>
<p><font face=Verdana>（3）、需要统一的认证，权限信息库。通常，认证与授权管理模块以一种应用专有的方式实现，系统的授权模型、认证，授权信息存贮结构与访问控制逻辑与应用的业务逻辑之间耦合紧密。这种设计与实现方式的缺点是显而易见的：由于认证、授权模块与应用逻辑之间的紧耦合使得认证、授权模块很难进行扩展与维护；认证、授权模块的设计与编码需要很大的工作量，而且很难在不同的应用系统之间共享与重用。这也是越来越多企业应用需要SSO的原因之一。</font></p>
<p><font face=Verdana>SSO要求有统一的认证，权限存放库。但现实中，有的系统无法使用外部的认证，授权信息库，所以就需要在应用系统和Portal Server之间进行认证，同时进行授权信息的数据同步。</font></p>
<p><font face=Verdana>2、实现描述：在用户成功登录 weblogic Portal之后，系统提供的Login Delegate机制来为用户登录到其有权可以使用的应用系统。系统提供Logout Delegate机制实现用户的注销功能（即SSO logout）。</font></p>
<p><font face=Verdana>用户存取由Portal SSO保护的若干资源，SSO会话服务（Session/SSO Service）提供了授权的证明，这样就不再需要用户重新进行身份验证了。在这种方式下，即使用户要访问不同的域（weblogic domain）的应用，我们提供的Portal SSO Service为其保持会话服务。</font></p>
<p><font face=Verdana>同时，SSO还包括的与登录恰恰相反的，统一的注销点，即用户一旦从Portal注销，则亦当从所有参与Portal SSO的应用注销。此处有一个例外，就是当用户从Portal登录并转向一个应用后，经过一段时间后可能会出现用户的该应用的会话还有效时用户的Portal会话过期时，此时用户将只能使用该应用，直到该用户再次登录Portal。</font></p>
<p><font face=Verdana>3、通过SSO Agent：当用户试图通过<a href="http://wiki.ccw.com.cn/index.php/%E6%B5%8F%E8%A7%88%E5%99%A8" target=_blank><font color=#0000cc><strong><a href="http://wiki.ccw.com.cn/%E6%B5%8F%E8%A7%88%E5%99%A8" target=_blank><font color=#0000cc><strong><u>浏览器</u></strong></font></a></strong></font></a>存取受保护的应用资源时，系统提供安装在不同应用上的SSO Agent来截取用户对资源的请求，并检查请求是否存在会话标识符，即token。如果token不存在，请求就被传递给Portal SSO，在Portal SSO上会话服务负责创建会话token，然后认证服务将提供登陆页面以验证用户。</font></p>
<p><font face=Verdana>4、创建会话Token：在用户身份验证之前，会话服务就创建了会话token。token为随机产生的Portal&nbsp; Server 会话标识符，该标识符代表了一个确定用户的特定会话。创建会话token后，认证服务把token插入cookie中在用户的浏览器中设定cookie。在token被设定的同时，该用户将会看到一个登陆页面。</font></p>
<p><font face=Verdana>5、用户认证：用户收到登陆页面和会话token后，填入合适的认证信息。当用户提交登陆页面后，这些认证信息就被发给认证提供者（authentication <a href="http://wiki.ccw.com.cn/Mac+Pro" target=_blank><font color=#0000cc><strong><u>pro</u></strong></font></a>vider）（LDAP服务器，RADIUS服务器等）进行验证。一旦认证提供者成功验证了认证信息，用户就被认为是通过了认证。Identity Server会从用户的token中取出会话信息并将会话状态设为有效。此后，用户就可以访问这些受保护的资源。</font></p>
<p><font face=Verdana>6、cookie和会话token：Cookie是由Web服务器创建的信息包，并传递给浏览器。Cookie 保存类似用户习惯等Web服务器产生的信息。它本身并不表明用户通过了认证。Cookie是特定于某个域的。在Identity Server的实现中，cookie由会话服务产生，并由认证服务设定。而且，Identity Server的cookie是会话cookie，存储在内存中。会话token由会话服务创建并插入Cookie。会话token利用安全随机数发生器产生，并包含Portal Server特有的会话信息。在存取受保护的资源之前，用户由认证服务验证，并创建SSO token。</font></p>
<p><font face=Verdana>二、SSO技术实现</font></p>
<p><font face=Verdana>1、SSO entry</font></p>
<p><font face=Verdana>SSO entry 作为系统唯一的登陆点将完成如下的功能：</font></p>
<p><font face=Verdana>（1）提供用户身份认证的登陆页面；<br>（2）根据用户的权限，显示可供用户使用的应用系统；<br>（3）调用用户选择的应用系统（B/S或C/S均可）；<br>（4）为应用系统传递SSO Token（针对不同的C/S，B/S应用可能还需要传递用户名，口令）；<br>（5）关闭SSO；<br>（6）SSO token失效后，关闭SSO entry。</font></p>
<p><font face=Verdana>注意SSO entry 并不提供通知应用系统SSO token已失效的功能，这一问题并不大，因为SSO token失效后，SSO entry 已被关闭。用户只有重新认证，才能获取新的SSO token。</font></p>
<p><font face=Verdana>2、SSO 部署</font></p>
<p><font face=Verdana><img src="http://www.ccw.com.cn/resources/cio/200410/20041028110732135.jpg" border=0><br>&nbsp;<br>上面是整个系统的部署的示意图。白色的立方体表示应用系统。浅蓝色的立方体表示Portal SSO平台。从上图可以看出：各个应用系统各个应用系统与SSO entry发生联系。SSO entry 需要向应用系统传递SSO Token，用户名，口令；启动应用系统。</font></p>
<p><font face=Verdana>3、SSO实现方式</font></p>
<p><font face=Verdana>SSO 的实现方式按照应用系统与Portal Server的交互程度分为三种。需要指出这些SSO的实现方式并没有优略之分，不同的实现方式适应不同的应用环境。解决现实中遇到的不同问题。真单点登陆： 通过SSO entry认证后，应用系统利用SSO API同Portal Server通信，验证用户是否经过认证。新开发的应用系统大多使用这种SSO的实现方式。这种方式需要对应用系统的认证模块进行修改。伪单点登陆：通过SSO entry认证后，应用系统还要自己进行用户的认证。这种方式适用于那些无法修改认证模块的应用系统。我们应用系统的整合采用该方式进行。整合第三方软件的单点登陆：Tarantella和Citrix都是将C/S应用系统转化为Web应用的第三方软件系统。在某些应用环境下，这类软件将发挥其作用。这种SSO实现方式在实施中并不使用，列出这种使用方式主要用来展现Identity Server的扩展性。</font></p>
<p><font face=Verdana>4、SSO的实现</font></p>
<p><font face=Verdana>（1）用户从Portal登录</font></p>
<p><font face=Verdana><img src="http://www.ccw.com.cn/resources/cio/200410/20041028110532639.jpg" border=0><br></font><font face=Verdana>说明：以上蓝色区域为Portal Server上的服务，白色为需要与Portal进行SSO的应用，绿色为每个应用的SSO Entry。代理登录的具体过程：用户首先登录Portal，Portal处理完登录后，启动代理登录，为用户登录应用系统，Portal把用户转向到应用系统，应用系统再把用户在征管系统的sessionID转发给Portal，登录代理（Login Delegate）把用户在征管系统中的sessionID和用户id发到征管系统的SSO Service进行登记，Portal把用户访问转向征管系统SSOEntry，征管系统SSOEntry通过用户的sessionID得到SSOService中记录的用户在Portal的用户userID，并把当前用户认可为此Portal userID对应的在征管系统中的用户，征管系统把用户访问转回Portal。接着登录代理再对SSO应用列表中的其它应用进行代理登录。代理登录完成，用户被重定向到Portal的相关页面。</font></p>
<p><font face=Verdana>用户访问应用系统：之后，当用户访问征管的某个应用时，征管系统发现该用户还未经认证，则将其sessionID与SSOService中登记的uid/sessionID比较，如果存在匹配，则认证通过，将用户id与session相关联。之后用户就可以直接访问征管系统了。</font></p>
<p><font face=Verdana>（2）SSO单点注销</font></p>
<p><font face=Verdana><img src="http://www.ccw.com.cn/resources/cio/200410/20041028110633123.jpg" border=0><br>&nbsp;<br>说明：</font></p>
<p><font face=Verdana>（1）用户注销总是从Portal开始；<br>（2）注销代理（Logout Delegate）将注销消息发到各应用的SSO Logout Entry；<br>（3）应用的SSO Logout Entry注销本地的用户会话记录；<br>（4）注销Portal上的用户会话记录。</font></p>
<p><font face=Verdana>三、安全性分析</font></p>
<p><font face=Verdana>Portal SSO的安全性需要从几个方面理解：</font></p>
<p><font face=Verdana>1.客户登录Portal Server之后SSO到应用的安全性，即SSOToken的安全性；<br>2.客户无SSOToken时，首先访问应用的安全性；<br>3.客户有SSOToken时想通过仿冒别人的有效SSOToken来进行非法的访问。</font></p>
<p><font face=Verdana>Portal Server生成的SSO token 因为需要在应用程序间传递，所以SSOToken比较容易被窃取，但Portal Server保证了SSOToken的唯一性，那些盗用合法SSOToken的人无法通过SSOToken的验证。因为SSOToken的生成收集了用户端和服务器端的很多信息，当非法用户持有效的SSOToken试图访问某应用时，该应用将通过Portal Server的SSOService进行SSOToken验证时，SSOToken的这些信息以及Portal Server的安全算法确保了盗用者即使拿到合法用户的SSOToken也无法通过SSOToken的有效性验证。所以SSOToken的安全性也得到了保证。</font></p>
<p><font face=Verdana>用户没有SSOToken的时候，如果客户要访问某应用，如果用户没有该应用的会话信息也没有Portal的SSOToken，则该用户必须先通过Portal的认证并SSO到该应用；如果用户已经在应用登录成功（可能是该应用保留了原有的认证入口，用户通过该入口访问此应用，也有可能是用户从Portal 登录后，通过SSO访问该应用，经过一段时间后，用户的Portal SSOToken已经过期，但是用户在该应用中的会话仍然保持有效），则用户可以继续使用该应用系统而无需重新认证。但是如果用户想访问Portal或者与Portal进行SSO的其它应用的话，则用户须经过Portal的认证并拿到SSOToken。所以在这种情况下SSOToken也是安全的。</font></p>
<p><font face=Verdana>每个用户的SSOToken只在其当次会话过程中有效，离开其当次会话，SSOToken即被视作无效，因此这种情况下也是安全的。</font></p>
<img src ="http://www.blogjava.net/junky/aggbug/118909.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-21 16:07 <a href="http://www.blogjava.net/junky/archive/2007/05/21/118909.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>作为Pattern Application集成Jive Forum的单点登录</title><link>http://www.blogjava.net/junky/archive/2007/05/21/118904.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Mon, 21 May 2007 07:59:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/21/118904.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/118904.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/21/118904.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/118904.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/118904.html</trackback:ping><description><![CDATA[<p>Jive Forum支持LDAP的认证方式，它有两种基本模式，一是Mixed Ldap模式，即与认证相关的用户信息存放在LDAP数据库中，而用户的其它信息仍存储于关系数据库中；另一种是Pure Ldap模式，即所有用户信息都存放于LDAP中，但其在部署实现上较为繁琐，需要扩展LDAP数据库的Schema。本文主要针对第一种模式部署的Jive Forum进行SSO测试，对于后一种模式效果应该完全相同。</p>
<p>在Jive Forum部署时，需使用Oracle Internet Directory（OID）作为LDAP目录数据库，位于搜索路径下的所有用户都可以登录Jive Forum，在配置时可以指定一名管理员，并在Oracle数据库JIVEUSERPREP表中用相应用户的USERID替换原本admin的USERID。随后按以下步骤实施即可。</p>
<p><a name=introduction>
<p>&nbsp;</p>
</a>Introduction
<p>&#160;</p>
<p>This document describes how you should configure Jive Forums Enterprise Edition to leverage Oracle Identity Management for maintaining the user credentials.&nbsp; It also describes how you can register the OracleAS Provider for Jive Forums with your OracleAS Portal and how to add the provider's portlets to your portal page.&nbsp; You should use these instructions if your Jive Forum Enterprise Edition Application is deployed on the OracleAS instance.</p>
<p><span>Note:</span> This feature is not available on the Professional Editions of Jive Forums.</p>
<p>&#160;</p>
<p><a name=configuration_details>
<p>&nbsp;</p>
</a>Configuring Jive Forums to Leverage Oracle Identity Management
<p>&#160;</p>
<p><a name=ldap>
<p>&nbsp;</p>
</a>Oracle Identity Management
<p>&#160;</p>
<p>It is possible to configure Jive Forums to use Oracle Identity Management as an external LDAP server to authenticate and store user profile information. This configuration ensures that the user entries are managed by a single server. Please refer to your Jive Forums Installation Document for further information on how to configure the Jive Forums application to work in the LDAP mode. In order to configure the LDAP mode, you will require to gather the following information about the Oracle Identity Management, against which you are authenticating your users.<br></p>
<ul>
    <li>The Identity Management host<br>
    <li>The Identity Management port<br>
    <li>The base DN for searching the users. </li>
</ul>
<p>The above information can be obtained from OracleAS Portal, available on the <span>Builder Page,</span> under <span>Administer-&gt;Global Settings.</span><br><br><span>Once you have your Jive Forums application running successfully in the LDAP mode, make the following changes to the <span>jive_config.xml</span> file in your <span>jiveHome</span> directory. Replace the existing entry for the <span>&lt;AuthFactory&gt;</span> element with the following one.<br></span></p>
<p><span>&lt;AuthFactory&gt;</span><br><span>&nbsp;&nbsp;&nbsp;&nbsp; &lt;className&gt;oracle.webdb.ohs.forum.security.PortalLdapAuthFactory&lt;/className&gt;</span><br><span>&lt;/AuthFactory&gt;</span><br></p>
<p><a name=single_sign_on>
<p>&nbsp;</p>
</a>Single Sign-On
<p>&#160;</p>
<p>It is also possible to have an Oracle Single Sign-On (SSO) server authenticate the users for the Jive Forums application. This functionality can be achieved by modifying the login / logout actions of the Jive Forums application. In order to enable the SSO feature, you have to create a file named <span>custom-actions.xml</span> file in the <span>WEB-INF/classes</span> directory, with the following content:<br></p>
<p><span>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br><br>&lt;actions&gt;<br><br>&nbsp; &lt;!-- Base actions --&gt;<br>&nbsp; &lt;action name="oracle.webdb.ohs.forum.actions.SSOLoginAction" alias="login"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;view name="success"&gt;doRedirect.jspa?url={jive.login.successURL}&lt;/view&gt;<br>&nbsp;&nbsp;&nbsp; &lt;view name="cancel"&gt;doRedirect.jspa?url={jive.login.cancelURL}&lt;/view&gt;<br>&nbsp;&nbsp;&nbsp; &lt;view name="fatal"&gt;doRedirect.jspa?url=index.jsp&lt;/view&gt;<br>&nbsp; &lt;/action&gt;<br>&nbsp;<br>&nbsp; &lt;action name="oracle.webdb.ohs.forum.actions.SSOLogoutAction" alias="logout"&gt;<br>&nbsp;&nbsp;&nbsp; &lt;view name="success"&gt;logout-success.jsp&lt;/view&gt;<br>&nbsp;&nbsp;&nbsp; &lt;view name="error"&gt;error.jsp&lt;/view&gt;<br>&nbsp; &lt;/action&gt;<br><br>&lt;/actions&gt;</span></p>
<p><span><br>Note:</span> If the file already exists, then Copy and Paste the content within the <span>&lt;actions&gt;</span> tag, into the <span>custom-actions.xml</span> file.<br></p>
<p><br>The <span>&lt;OC4J_Instance&gt;</span> would need to be restarted for the changes to take effect, where <span>&lt;OC4J_Instance&gt;</span> is the <span>OC4J instance</span> where Jive Forum is installed. </p>
<p><a name=registering_the_provider>
<p>&nbsp;</p>
</a>Registering the Provider
<p>&#160;</p>
<ol>
    <li>Login to OracleAS Portal as an OracleAS Portal Administrator.&nbsp; Go to the <font color=#336699>Administer </font>tab of the OracleAS Portal <font color=#336699>Builder </font>page.
    <li>Under the&nbsp;<span><font face="Courier New" color=#336699>Portlets</font></span> tab, click on&nbsp;<span><font face="Courier New" color=#336699>Register a Provider</font></span> within the <span><font face="Courier New" color=#336699>Remote Providers </font></span>portlet.
    <li>
    <p>Fill out provider information for the&nbsp; Provider: </p>
    <ul>
        <li><font face="Courier New, Courier, monospace"><font color=#336699>Name</font></font>: OracleAS_Provider_for_Jive_Forums
        <li><font face="Courier New, Courier, monospace"><font color=#336699>Display Name</font></font>: OracleAS Provider for Jive Forums
        <li><font face="Courier New, Courier, monospace"><font color=#336699>Timeout</font></font>: 30
        <li><font face="Courier New, Courier, monospace"><font color=#336699>Timeout Message</font></font>: OracleAS Provider for Jive Forums Timed Out
        <li><font face="Courier New, Courier, monospace"><font color=#336699>Implementation Style</font></font>: Web </li>
    </ul>
    <li>Click on the <font face="Courier New, Courier, monospace"><font color=#336699>Next</font></font> button to enter the Web Provider specific information
    <li>
    <p>Enter the <font face="Courier New, Courier, monospace"><font color=#336699>Web Provider location</font></font> </p>
    <ul>
        <li><span>URL: http://&lt;server&gt;:&lt;port&gt;/&lt;jiveContext&gt;/providers/forum</span> </li>
    </ul>
    </li>
</ol>
<p>example: if your jive url looks like, <span>http://my.company.com/jive3</span>, then your web provider location is <span>http://my.company.com/jive3/providers/forum</span><br></p>
<ol start=6>
    <li>Select the radio button labeled <font size=+0><span>The user has the same identity in the Web providers application as in the Single Sign-On identity</span>.</font>
    <li>Scroll down to the <font face="Courier New, Courier, monospace"><font color=#336699>User/Session Information</font></font> section and specify the login frequency as <font face="Courier New, Courier, monospace"><font color=#336699>Once Per Session.</font></font>
    <li>Click on the <font face="Courier New, Courier, monospace"><font color=#336699>Finish</font></font> button to complete the registration </li>
</ol>
<p><strong>Note:</strong> When registering a new provider with OracleAS Portal, only the user who registered the provider has privileges to see the provider/portlets. If necessary, go to the Folder with the name of the provider within the Portlet Repository content area and update the provider privileges as required. <br></p>
<p><a name=Adding_the_Jive_Portlets_to_Portal_Page>
<p>&nbsp;</p>
</a>Adding the OracleAS Provider for Jive Forums' Portlets to a Portal Page
<p>&#160;</p>
<p>You are now ready to add the&nbsp;OracleAS Provider for Jive Forums Portlets to a page.<br></p>
<img src ="http://www.blogjava.net/junky/aggbug/118904.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-21 15:59 <a href="http://www.blogjava.net/junky/archive/2007/05/21/118904.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CAS 反向认证机制详解</title><link>http://www.blogjava.net/junky/archive/2007/05/21/118902.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Mon, 21 May 2007 07:53:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/21/118902.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/118902.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/21/118902.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/118902.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/118902.html</trackback:ping><description><![CDATA[<p class=paragraph>A manual walkthrough of CAS proxy tickets.<br>This walkthrough was provided by David Spencer on the CAS Mailman list.</p>
<p class=heading2><a name=ProxyCASWalkthrough-Introduction>Introduction</a></p>
<p>When I was trying to understand the mechanisms involved in writing proxying applications using CAS, I found it very helpful to manually walkthrough the aquisition of a proxy ticket. The CAS server played itself in this exercise and I played all the other roles - user, proxying application and proxied application - simply by constructing URLs and feeding them into a web browser. </p>
<p class=paragraph>The only part of the exercise that can't be done with just a web browser and careful URL construction is the part where CAS makes it's own callback to the proxying application. For this, I chose a proxy callback url on a machine for which I had access to the log files and scanned through the HTTP requests to find the information I wanted.</p>
<p class=heading3><a name=ProxyCASWalkthrough-StepOne%3Alogin>Step One: login</a></p>
<p>To start with, log in to CAS with some invented service:<br></p>
<p class=preformattedContent>https://foo.bar.com/is/cas/login?service=http://localhost/bling</p>
<p class=paragraph>On successful login, CAS will redirect you to the service with a ticket appended (it doesn't matter that the service is made up as the ticket you're after is part of the url and will appear in the location bar even if your browser can't find the resource):</p>
<p class=preformattedContent>http://localhost/bling?ticket=ST-956-Lyg0BdLkgdrBO9W17bXS</p>
<p class=paragraph></p>
<p class=heading3><a name=ProxyCASWalkthrough-StepTwo>Step Two </a></p>
<p>&#160;</p>
<p class=heading4><a name=ProxyCASWalkthrough-%28a%29%3Averifytheticketandbedone>(a): verify the ticket and be done</a></p>
<p class=paragraph>So, playing the role of the first application (not a proxying application at this stage - lets just see if we can get our application authenticated without proxying for now), you need to take the ticket and turn it into a username:</p>
<p class=preformattedContent>https://foo.bar.com/is/cas/serviceValidate?ticket=ST-956-Lyg0BdLkgdrBO9W17bXS&amp;service=http://localhost/bling</p>
<p class=paragraph>which will produce a result like:</p>
<p class=codeContent><span class=xml-tag>&lt;cas:serviceResponse <span class=xml-keyword>xmlns:cas</span>='http://www.yale.edu/tp/cas'&gt;</span> <span class=xml-tag>&lt;cas:authenticationSuccess&gt;</span> <span class=xml-tag>&lt;cas:user&gt;</span>endjs<span class=xml-tag>&lt;/cas:user&gt;</span> <span class=xml-tag>&lt;/cas:authenticationSuccess&gt;</span><span class=xml-tag>&lt;/cas:serviceResponse&gt;</span></p>
<p class=paragraph>This is the end of the road for normal applications that don't need to proxy other applications.</p>
<p class=heading4><a name=ProxyCASWalkthrough-StepTwo%28b%29%3Averifytheticketandenablefurtherproxying>Step Two (b): verify the ticket and enable further proxying</a></p>
<p class=paragraph>If instead you do want to be able to proxy other applications you need to also supply a pgtUrl to your validation request so that CAS can callback with the Proxy Granting Ticket. This is where life gets complicated, especially if you forget that service tickets are one-time-only tickets and that once you've used them with serviceValidate, you have to go back to CAS and get a new one (so if you've done Step One and Step Two (a) you'll need to do Step One again before you can do Step Two (b)).</p>
<p>The choice of pgtUrl here is fairly arbitrary except that it needs to be an https url and it needs to be on a server on which you can access the log files. <br></p>
<p class=preformattedContent>https://foo.bar.com/is/cas/serviceValidate?ticket=ST-956-Lyg0BdLkgdrBO9W17bXS&amp;service=http://localhost/bling&amp;pgtUrl=https://foo.bar.com/pgtCallback</p>
<p class=paragraph>results in:</p>
<p class=codeContent><span class=xml-tag>&lt;cas:serviceResponse <span class=xml-keyword>xmlns:cas</span>='http://www.yale.edu/tp/cas'&gt;</span> <span class=xml-tag>&lt;cas:authenticationSuccess&gt;</span> <span class=xml-tag>&lt;cas:user&gt;</span>endjs<span class=xml-tag>&lt;/cas:user&gt;</span><span class=xml-tag>&lt;cas:proxyGrantingTicket&gt;</span>PGTIOU-85-8PFx8qipjkWYDbuBbNJ1roVu4yeb9WJIRdngg7fzl523Eti2td<span class=xml-tag>&lt;/cas:proxyGrantingTicket&gt;</span> <span class=xml-tag>&lt;/cas:authenticationSuccess&gt;</span><span class=xml-tag>&lt;/cas:serviceResponse&gt;</span></p>
<p class=paragraph></p>
<p class=heading3><a name=ProxyCASWalkthrough-StepThree%3AdigoutthePGT>Step Three: dig out the PGT</a></p>
<p>Now our first application knows who the user is and has a Proxy Granting Ticket IOU. To find the real PGT we look in the apache access log for foo.bar.com and hunt out the request made by CAS to deliver the PGT: </p>
<p>&#160;</p>
<p class=preformattedContent>foo.bar.com - - [10/Dec/2003:09:28:15 +0000] "GET/pgtCallback?pgtIou=PGTIOU-85-8PFx8qipjkWYDbuBbNJ1roVu4yeb9WJIRdngg7fzl523Eti2td&amp;pgtId=PGT-330-CSdUc5fCBz3g8KDDiSgO5osXfLMj9sRDAI0xDLg7jPn8gZaDqS HTTP/1.1" 200 13079</p>
<p><br>(Editor's note: linebreaks introduced for page formatting.) </p>
<p class=paragraph></p>
<p class=heading3><a name=ProxyCASWalkthrough-StepFour%3Agetaproxyticket>Step Four: get a proxy ticket</a></p>
<p>With the PGT in our grasp we can make a call on CAS to give us a proxy ticket for some other service we wish to proxy: </p>
<p>&#160;</p>
<p class=preformattedContent>https://foo.bar.com/is/cas/proxy?targetService=http://localhost/bongo&amp;pgt=PGT-330-CSdUc5fCBz3g8KDDiSgO5osXfLMj9sRDAI0xDLg7jPn8gZaDqS</p>
<p class=paragraph>resulting in:</p>
<p class=codeContent><span class=xml-tag>&lt;cas:serviceResponse&gt;</span> <span class=xml-tag>&lt;cas:proxySuccess&gt;</span> <span class=xml-tag>&lt;cas:proxyTicket&gt;</span>PT-957-ZuucXqTZ1YcJw81T3dxf<span class=xml-tag>&lt;/cas:proxyTicket&gt;</span> <span class=xml-tag>&lt;/cas:proxySuccess&gt;</span><span class=xml-tag>&lt;/cas:serviceResponse&gt;</span></p>
<p class=paragraph></p>
<p class=heading3><a name=ProxyCASWalkthrough-StepFive%3Averifytheproxyticket>Step Five: verify the proxy ticket</a></p>
<p>Now we take on our final role for the exercise - the proxied application. The proxying application has invoked our service url and has passed in the proxy ticket it's got. We take that ticket and validate it to find out both who the user is and which applications are in the proxy chain: </p>
<p>&#160;</p>
<p class=preformattedContent>https://foo.bar.com/is/cas/proxyValidate?service=http://localhost/bongo&amp;ticket=PT-957-ZuucXqTZ1YcJw81T3dxf</p>
<p class=paragraph>resulting in:</p>
<p class=codeContent><span class=xml-tag>&lt;cas:serviceResponse <span class=xml-keyword>xmlns:cas</span>='http://www.yale.edu/tp/cas'&gt;</span> <span class=xml-tag>&lt;cas:authenticationSuccess&gt;</span> <span class=xml-tag>&lt;cas:user&gt;</span>endjs<span class=xml-tag>&lt;/cas:user&gt;</span> <span class=xml-tag>&lt;cas:proxies&gt;</span> <span class=xml-tag>&lt;cas:proxy&gt;</span>https://foo.bar.com/pgtCallback<span class=xml-tag>&lt;/cas:proxy&gt;</span> <span class=xml-tag>&lt;/cas:proxies&gt;</span> <span class=xml-tag>&lt;/cas:authenticationSuccess&gt;</span><span class=xml-tag>&lt;/cas:serviceResponse&gt;</span></p>
<p class=paragraph>Obviously, this walkthrough doesn't help with acquiring and plugging in good proxying code for your application but it does help to see what the proxying code needs to be doing and makes it easier to write your own.</p>
<p>Originally provided by: David Spencer on the CAS mailing list.<br></p>
<!--<rdf:rdf xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"         xmlns:dc="http://purl.org/dc/elements/1.1/"         xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"><rdf:description    rdf:about="http://jasigch.princeton.edu:9000/display/CAS/Proxy+CAS+Walkthrough"    dc:identifier="http://jasigch.princeton.edu:9000/display/CAS/Proxy+CAS+Walkthrough"    dc:title="Proxy CAS Walkthrough"    trackback:ping="http://jasigch.princeton.edu:9000/rpc/trackback/729"/></rdf:rdf>-->
<img src ="http://www.blogjava.net/junky/aggbug/118902.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-21 15:53 <a href="http://www.blogjava.net/junky/archive/2007/05/21/118902.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CAS 2.0 的体系结构 </title><link>http://www.blogjava.net/junky/archive/2007/05/21/118900.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Mon, 21 May 2007 07:50:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/21/118900.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/118900.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/21/118900.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/118900.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/118900.html</trackback:ping><description><![CDATA[<h2 class=heading2 style="MARGIN: 4px 0px"><a name=CAS2.0Architecture-Status>Status</a></h2>
<p>CAS 2.0 is the version of CAS currently in production at Yale University. CAS 2.0 is backwards compatible with CAS 1.0; it simply introduces new features. Application developers at Yale need only understand the concepts and techniques in the CAS 1.0 paper to integrate with CAS successfully, unless their applications need to proxy credentials (as is the case with portals, web-mail services, and so forth). Unless you develop such applications, you will likely be more interested in the original <a title="CAS 1.0 Architecture" href="http://jasigch.princeton.edu:9000/display/CAS/CAS+1.0+Architecture"><u><font color=#0000ff>CAS 1.0 Architecture</font></u></a> document. </p>
<p>&#160;</p>
<h2 class=heading2><a name=CAS2.0Architecture-Motivationforupgrade>Motivation for upgrade</a></h2>
In a multi-tier CAS installation, an agent acting on behalf of a user, but without direct access to the user's cookie cache, may need to convince a third party that it represents the user legitimately. There are two requirements that must be met for any such convincing to be convincing:
<ul class=star>
    <li>The agent must establish its own identity
    <li>The agent must demonstrate that it has been in contact with the user "recently."<br>The first criterion alone is insufficient because it is undesirable for even a relatively trusted agent to be able to impersonate all users baselessly; the second is likewise inadequate because not every agent in contact with the user is necessarily trusted to represent that user to any given third party. (Indeed, CAS specifically prevents two unrelated services from sharing credentials meaningfully.) </li>
</ul>
In practice (and in the absense of an explicit user logout), it is not straightforward to determine whether a user is still present and desirous of authenticated services. Thus, we settle on "recent contact" as a basis for assuming the user has given implicit pemission for credentials to be proxied. (Such "permission" is not a blanket license for any proxy agent to impersonate the user to the world; as we'll see, a back-end service must explicitly trust the "proxy path" of a credential.)
<p class=paragraph>(As a side note, since "contact" with a service protected by the Central Authentication Service doesn't necessarily involve any affirmative action on the part of the user, the "contact with service" provision could be dropped, and a simpler requirement of "contact with CAS itself" could be substituted in its place. Under such a model, a proxy could simply authenticate itself to a back-end target service and assert that it acts on behalf of any user who recently authenticated with CAS. However, we prefer a model where CAS &#8211; instead of back-end services &#8211; may authenticate proxy agents. As usual, CAS can centralize authentication procedures, resulting in simpler management and use.)</p>
In this document, we explain an extension to CAS 1.0 that accommodates the two requirements discussed above. When these two requirements have been met, a back-end service may determine (a) what user is being impersonated; (b) who is handling the impersonation. These two determinations allow service to accept proxied credentials selectively.
<p class=paragraph></p>
<h2 class=heading2><a name=CAS2.0Architecture-Theplayers>The players</a></h2>
<p class=heading2 align=center>
<table cellSpacing=0 cellPadding=0 width="68%" align=center border=1>
    <tbody>
        <tr>
            <td>
            <p align=center><font size=2>&nbsp;<strong>Player</strong></font></p>
            </td>
            <td>
            <p align=center><strong><font size=2>&nbsp;Role</font></strong></p>
            </td>
        </tr>
        <tr>
            <td><font size=2>&nbsp;CAS = The Central Authentication Service</font></td>
            <td><font size=2>&nbsp;a trusted arbiter of authenticity</font></td>
        </tr>
        <tr>
            <td><font size=2>&nbsp;Service</font></td>
            <td><font size=2>&nbsp;A web application that authenticates users via CAS.</font></td>
        </tr>
        <tr>
            <td><font size=2>&nbsp;Proxy</font></td>
            <td><font size=2>&nbsp;A service that wants to access other services on behalf of a particular user.</font></td>
        </tr>
        <tr>
            <td><font size=2>&nbsp;Target (or back-end service)</font></td>
            <td><font size=2>&nbsp;A service that accepts proxied credentials from at least one particular proxy.</font></td>
        </tr>
    </tbody>
</table>
</p>
<h2 class=heading2><a name=CAS2.0Architecture-Tickets>Tickets</a></h2>
<p>Parties involved in a CAS authentication make use of tickets, or opaque strings that prove some assertion to CAS. CAS 2.0 uses the following tickets: </p>
<p align=center>
<table cellSpacing=1 cellPadding=1 width="68%" border=1>
    <tbody>
        <tr>
            <td>
            <p align=center>&nbsp;<strong><font size=2>Ticket type</font></strong></p>
            </td>
            <td>
            <p align=center><font size=2>&nbsp;<strong>description</strong></font></p>
            </td>
        </tr>
        <tr>
            <td><font size=2>&nbsp;Ticket-granting cookie (TGC)</font></td>
            <td><font size=2>&nbsp;A ticket encapulated by a cookie that is sent to the user's web browser and returned only to CAS, and only over a secured channel. This (optional) ticket establishes the user's identity with CAS and lets CAS act as a single sign-on system for the web.</font></td>
        </tr>
        <tr>
            <td><font size=2>&nbsp;Service ticket (ST)</font></td>
            <td><font size=2>&nbsp;A ticket sent by CAS, through the user's browser, to a service. Each ST may be used only once, and must be combined with the unique identifier for one specific service in order to be useful. (Put another way, a service that knows its own unique identifier will refuse to accept STs intended for another service. This prevents one service from mounting a "man in the middle" attack against another.)</font></td>
        </tr>
        <tr>
            <td><font size=2>&nbsp;Proxy-granting ticket (PGT)</font></td>
            <td><font size=2>&nbsp;A ticket sent by CAS to a service holding a valid ST (but see below). This ticket (associated with an individual service and an individual user) confers the ability to produce proxy tickets (see below)</font></td>
        </tr>
        <tr>
            <td><font size=2>&nbsp;Proxy-granting ticket IOU (PGTIOU)</font></td>
            <td><font size=2>&nbsp;A ticket sent by CAS alone in a service validation response, and with a PGT to the callback URL. It is the web application's responsibility to maintain a table to correlate PGTIOUs and PGTs (see below)</font></td>
        </tr>
        <tr>
            <td><font size=2>&nbsp;Proxy ticket (PT)</font></td>
            <td><font size=2>&nbsp;A ticket usable by a proxy to access a target by impersonating a single user. The PT carries information about the proxy or proxies attempting to gain access. For targets that are also (second- or higher-level proxies), a PT may be used to obtain a PGT, but this PGT will preserve information about the linear series of proxies that lie between the user and the ultimate target.</font></td>
        </tr>
    </tbody>
</table>
</p>
<h2 class=heading2><a name=CAS2.0Architecture-Proxymechanism%3A>Proxy mechanism:</a></h2>
<p>For the purposes of the this document, it's assumed you're familiar with CAS 1.0's underlying processes, which are described in more detail in the ITS Central Authentication Service paper. CAS's basic mechanism can be diagrammed as follows:<br>&nbsp;</p>
<p align=center><img alt="" src="http://blog.csdn.net/images/blog_csdn_net/yuanxz/99227/o_cas1.jpg"></p>
<p>&#160;</p>
<p>&#160;</p>
In this diagram, the actual protocol implemented by a web application that wishes to use CAS is represented by the thick gray line at the top. In version 1.0, the protocol works as follows:
<p class=paragraph>Request: The web application sends an HTTP request with two query parameters: ticket and service, representing respectively an ST and the unique identifier (URL) corresponding to the web application.</p>
Response: CAS responds with a message in one of two formats: "no\n" or "yes\nnetid", where NetID is the authentic NetID of the user.
<p class=paragraph>In recognition of XML's increasing popularity, CAS 2.0 supplements this existing protocol with an existing and optionally available one. The format of the request is the same as above, but the response takes the following format:</p>
<p>Response: CAS response with an XML document whose root element is &lt;cas:serviceResponse&gt;. On failure, the document node contains a single subelement:</p>
<div class=code>
<div class=codeContent>
<pre><span class=xml-tag>&lt;cas:serviceResponse <span class=xml-keyword>xmlns:cas</span>='http://www.yale.edu/tp/cas'&gt;</span>    <span class=xml-tag>&lt;cas:authenticationFailure code=<span class=xml-quote>"..."</span>&gt;</span>	Optional authentication failure message    <span class=xml-tag>&lt;/cas:authenticationFailure&gt;</span>  <span class=xml-tag>&lt;/cas:serviceResponse&gt;</span></pre>
</div>
</div>
<p class=paragraph>On success, the document node contains a subelement with its own marked-up content:</p>
<div class=code>
<div class=codeContent>
<pre><span class=xml-tag>&lt;cas:serviceResponse <span class=xml-keyword>xmlns:cas</span>='http://www.yale.edu/tp/cas'&gt;</span>    <span class=xml-tag>&lt;cas:authenticationSuccess&gt;</span>	<span class=xml-tag>&lt;cas:user&gt;</span>NetID<span class=xml-tag>&lt;/cas:user&gt;</span>    <span class=xml-tag>&lt;/cas:authenticationSuccess&gt;</span>  <span class=xml-tag>&lt;/cas:serviceResponse&gt;</span></pre>
</div>
</div>
<p class=paragraph>In addition to this core validation protocol, CAS 2.0 provides an additional protocol available to any web applications that wish to use it. As before, the format of the request is the same, but the response, on success, contains an extra element providing new information:</p>
<div class=code>
<div class=codeContent>
<pre><span class=xml-tag>&lt;cas:serviceResponse&gt;</span>    <span class=xml-tag>&lt;cas:authenticationSuccess&gt;</span>        <span class=xml-tag>&lt;cas:user&gt;</span>NetID<span class=xml-tag>&lt;/cas:user&gt;</span>        <span class=xml-tag>&lt;cas:proxyGrantingTicket&gt;</span>PGTIOU<span class=xml-tag>&lt;/cas:proxyGrantingTicket&gt;</span>    <span class=xml-tag>&lt;/cas:authenticationSuccess&gt;</span>  <span class=xml-tag>&lt;/cas:serviceResponse&gt;</span></pre>
</div>
</div>
<p class=paragraph>The proxyGrantingTicket element contains as its body content information identifying the PGT to be sent to the web application, essentially a PGT "IOU". This value is not the typical opaque identifier for the actual PGT; it is simply a unique value that indexes this PGT. Synchronously, CAS must send the PGT's actual (opaque) value as a request parameter to a URL owned by the application (and identified by the request parameter proxyCallbackUrl in the validation request to CAS). The callback request contains two query parameters: pgtId, containing the actual value representing the PGT, and pgtIou, containing the PGTIOU contained in CAS's response to the web application's request.</p>
(This indirection is necessary to let CAS validate the service using its server certificate, which was deemed the simplest way to provide for mutual authentication, in the field, of services. Custom certificates, and locally shared secrets, are other options, but both were deemed less convenient.
<p class=paragraph>Once this PGT has been received and matched appropriately by the web application, it enables a new protocol (exposed to web applications) that allows the retrieval of specific-use proxy tickets (PTs), also defined earlier:</p>
Request: The web application sends an HTTP request with two query parameters: pgt and targetService, representing respectively a PGT and the unique identifier (URL) corresponding to the target web application to which the present web application (i.e., the proxy) wishes to gain access.
<p class=paragraph>Response: A cas:proxyFailure message can be returned here for invalid or expired PGTs. On success, a message of the following format is sent by CAS to the application:</p>
<div class=code>
<div class=codeContent>
<pre><span class=xml-tag>&lt;cas:serviceResponse <span class=xml-keyword>xmlns:cas</span>='http://www.yale.edu/tp/cas'&gt;</span>    <span class=xml-tag>&lt;cas:proxySuccess&gt;</span>      <span class=xml-tag>&lt;cas:proxyTicket&gt;</span>PT<span class=xml-tag>&lt;/cas:proxyTicket&gt;</span>    <span class=xml-tag>&lt;/cas:proxySuccess&gt;</span>  <span class=xml-tag>&lt;/cas:serviceResponse&gt;</span></pre>
</div>
</div>
<p class=paragraph>This PT may then be sent by the application to the target service, which validates it with CAS using the following protocol.</p>
Request: (Same as above, for a "regular" service.)
<p class=paragraph>Response: As before, authenticationFailure is an option here. On success, a message with the following form is returned:</p>
<div class=code>
<div class=codeContent>
<pre><span class=xml-tag>&lt;cas:serviceResponse <span class=xml-keyword>xmlns:cas</span>='http://www.yale.edu/tp/cas'&gt;</span>    <span class=xml-tag>&lt;cas:authenticationSuccess&gt;</span>	<span class=xml-tag>&lt;cas:user&gt;</span>NetID<span class=xml-tag>&lt;/cas:user&gt;</span>        <span class=xml-tag>&lt;cas:proxyGrantingTicket&gt;</span>PGTIOU<span class=xml-tag>&lt;/cas:proxyGrantingTicket&gt;</span>	<span class=xml-tag>&lt;cas:proxies&gt;</span>	   <span class=xml-tag>&lt;cas:proxy&gt;</span>proxy1<span class=xml-tag>&lt;/cas:proxy&gt;</span>	   <span class=xml-tag>&lt;cas:proxy&gt;</span>proxy2<span class=xml-tag>&lt;/cas:proxy&gt;</span>	   <span class=xml-tag>&lt;cas:proxy&gt;</span>proxy3<span class=xml-tag>&lt;/cas:proxy&gt;</span>	   ...	<span class=xml-tag>&lt;/cas:proxies&gt;</span>    <span class=xml-tag>&lt;/cas:authenticationSuccess&gt;</span>  <span class=xml-tag>&lt;/cas:serviceResponse&gt;</span></pre>
</div>
</div>
<p class=paragraph>The target service may then make any access-control decisions it needs to make, based both on the username and the proxy "path" conveyed by the response from CAS.</p>
As the general format of this latest response message suggests, proxies may be "chained" &#8211; that is, applied successively &#8211; before an ultimate target is reached. This chaining is achieved by the added PGTIOU to this recent message &#8211; when target services implement the "proxy granting" protocol. Thus, a target service may act as a proxy as well.
<p class=paragraph></p>
<h3 class=heading3><a name=CAS2.0Architecture-MiscellaneousupdatestoCAS1.0>Miscellaneous updates to CAS 1.0</a></h3>
<p>&#160;</p>
In addition to proxiable credentials, CAS 2.0 introduces a few new features to the base CAS design:<br>
<ul class=star>
    <li>Users can now choose, from the login page, to be warned before CAS uses its single sign-on capabilities to log them into web applications. This facilitates anonymous web browsing in situations where this is a concern.
    <li>Applications that pass the value true for the request parameter renew when redirecting users to CAS's login page can explicitly choose to opt out of CAS's single sign-on facility. This is appropriate for applications that want to use CAS but want to ensure that the user is still present before displaying sensitive information. Normally, this behavior is simply advisory and does not ensure the user just recently logged in; however, if you pass the value true for the request parameter renew to the URL for service-ticket validation, CAS will fail to authenticate the user unless the service ticket you've passed was the result of a new primary authentication.
    <li>Applications that pass the value true for the request parameter "<a title=gateway href="http://jasigch.princeton.edu:9000/display/CAS/gateway"><u><font color=#0000ff>gateway</font></u></a>" when redirecting users to CAS's login page instruct CAS not to conduct a primary authentication. This is useful for applications that want to accept a pre-existing CAS ticket-granting cookie but do not otherwise want to interrupt their own page flow with CAS's login screen. (Such behavior is expected to be transitional.) </li>
</ul>
<h3 class=heading3><a name=CAS2.0Architecture-Restrictionsandotherchangesincludethefollowing%3A>Restrictions and other changes include the following:</a></h3>
<ul class=star>
    <li>Because of incompatibilities between different browsers, service URLs may not include encoded or bare representations of linefeed, carriage return, or double-quotation-mark characters. If a page in your application uses such characters in query strings, ensure that it is not the first page that requires CAS authentication.</li>
</ul>
<img src ="http://www.blogjava.net/junky/aggbug/118900.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-21 15:50 <a href="http://www.blogjava.net/junky/archive/2007/05/21/118900.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>LDAP</title><link>http://www.blogjava.net/junky/archive/2007/05/21/118852.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Mon, 21 May 2007 05:09:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/21/118852.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/118852.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/21/118852.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/118852.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/118852.html</trackback:ping><description><![CDATA[LDAP的英文全称是Lightweight Directory Access Protocol，一般都简称为LDAP。它是基于<a href="http://baike.baidu.com/view/487586.htm" target=_blank><u><font color=#0000ff>X.500</font></u></a>标准的，但是简单多了并且可以根据需要定制。与X.500不同，LDAP支持<a href="http://baike.baidu.com/view/7729.htm" target=_blank><u><font color=#0000ff>TCP/IP</font></u></a>，这对访问Internet是必须的。LDAP的核心规范在RFC中都有定义，所有与LDAP相关的RFC都可以在LDAPman RFC网页中找到。现在LDAP技术不仅发展得很快而且也是激动人心的。在企业范围内实现LDAP可以让运行在几乎所有计算机平台上的所有的应用程序从 LDAP目录中获取信息。LDAP目录中可以存储各种类型的数据：电子邮件地址、邮件路由信息、人力资源数据、公用密匙、联系人列表，等等。通过把 LDAP目录作为系统集成中的一个重要环节，可以简化员工在企业内部查询信息的步骤，甚至连主要的数据源都可以放在任何地方。<br><br>LDAP目录的优势<br><br>如果需要开发一种提供公共信息查询的系统一般的设计方法可能是采用基于WEB的数据库设计方式，即前端使用浏览器而后端使用WEB服务器加上关系数据库。后端在Windows的典型实现可能是Windows NT + IIS + Acess数据库或者是SQL服务器，IIS和数据库之间通过ASP技术使用ODBC进行连接，达到通过填写表单查询数据的功能；<br><br>后端在Linux系统的典型实现可能是Linux+ Apache + postgresql，Apache 和数据库之间通过PHP3提供的函数进行连接。使用上述方法的缺点是后端关系数据库的引入导致系统整体的性能降低和系统的管理比较繁琐，因为需要不断的进行数据类型的验证和事务的完整性的确认；并且前端用户对数据的控制不够灵活，用户权限的设置一般只能是设置在表一级而不是设置在记录一级。<br><br>目录服务的推出主要是解决上述数据库中存在的问题。目录与关系数据库相似，是指具有描述性的基于属性的记录集合，但它的数据类型主要是字符型，为了检索的需要添加了BIN （二进制数据）、CIS（忽略大小写）、CES（大小写敏感）、TEL（电话型）等语法（Syntax），而不是关系数据库提供的整数、浮点数、日期、货币等类型，同样也不提供象关系数据库中普遍包含的大量的函数，它主要面向数据的查询服务（查询和修改操作比一般是大于10:1），不提供事务的回滚（rollback）机制，它的数据修改使用简单的锁定机制实现All-or-Nothing，它的目标是快速响应和大容量查询并且提供多目录服务器的信息复制功能。<br><br>现在该说说LDAP目录到底有些什么优势了。现在LDAP的流行是很多因数共同作用的结果。可能LDAP最大的优势是：可以在任何计算机平台上，用很容易获得的而且数目不断增加的LDAP的客户端程序访问LDAP目录。而且也很容易定制应用程序为它加上LDAP的支持。<br><br>LDAP协议是跨平台的和标准的协议，因此应用程序就不用为LDAP目录放在什么样的服务器上操心了。实际上，LDAP得到了业界的广泛认可，因为它是Internet的标准。产商都很愿意在产品中加入对LDAP的支持，因为他们根本不用考虑另一端（客户端或服务端）是怎么样的。LDAP服务器可以是任何一个开发源代码或商用的LDAP目录服务器（或者还可能是具有LDAP界面的关系型数据库），因为可以用同样的协议、客户端连接软件包和查询命令与LDAP服务器进行交互。与LDAP不同的是，如果软件产商想在软件产品中集成对DBMS的支持，那么通常都要对每一个数据库服务器单独定制。不象很多商用的关系型数据库，你不必为LDAP的每一个客户端连接或许可协议付费 大多数的LDAP服务器安装起来很简单，也容易维护和优化。<br><br>LDAP服务器可以用&#8220;推&#8221;或&#8220;拉&#8221;的方法复制部分或全部数据，例如：可以把数据&#8220;推&#8221;到远程的办公室，以增加数据的安全性。复制技术是内置在LDAP服务器中的而且很容易配置。如果要在DBMS中使用相同的复制功能，数据库产商就会要你支付额外的费用，而且也很难管理。<br><br>LDAP允许你根据需要使用ACI（一般都称为ACL或者访问控制列表）控制对数据读和写的权限。例如，设备管理员可以有权改变员工的工作地点和办公室号码，但是不允许改变记录中其它的域。ACI可以根据谁访问数据、访问什么数据、数据存在什么地方以及其它对数据进行访问控制。因为这些都是由LDAP目录服务器完成的，所以不用担心在客户端的应用程序上是否要进行安全检查。<br><br>LDAP（Lightweight Directory Acess Protocol）是目录服务在TCP/IP上的实现（RFC 1777 V2版和RFC 2251<br><br>V3版）。它是对X500的目录协议的移植，但是简化了实现方法，所以称为轻量级的目录服务。在LDAP中目录是按照树型结构组织，目录由条目（Entry）组成，条目相当于关系数据库中表的记录；条目是具有区别名DN（Distinguished<br><br>Name）的属性（Attribute）集合，DN相当于关系数据库表中的关键字（Primary<br><br>Key）；属性由类型（Type）和多个值（Values）组成，相当于关系数据库中的域（Field）由域名和数据类型组成，只是为了方便检索的需要，LDAP中的Type可以有多个Value，而不是关系数据库中为降低数据的冗余性要求实现的各个域必须是不相关的。LDAP中条目的组织一般按照地理位置和组织关系进行组织，非常的直观。LDAP把数据存放在文件中，为提高效率可以使用基于索引的文件数据库，而不是关系数据库。LDAP协议集还规定了DN的命名方法、存取控制方法、搜索格式、复制方法、URL格式、开发接口等<br><br>LDAP对于这样存储这样的信息最为有用，也就是数据需要从不同的地点读取，但是不需要经常更新。<br><br>例如，这些信息存储在LDAP目录中是十分有效的：<br><br>l 公司员工的电话号码簿和组织结构图<br><br>l 客户的联系信息<br><br>l 计算机管理需要的信息，包括NIS映射、email假名，等等<br><br>l 软件包的配置信息<br><br>l 公用证书和安全密匙<br><br>什么时候该用LDAP存储数据<br><br>大多数的LDAP服务器都为读密集型的操作进行专门的优化。因此，当从LDAP服务器中读取数据的时候会比从专门为OLTP优化的关系型数据库中读取数据快一个数量级。也是因为专门为读的性能进行优化，大多数的LDAP目录服务器并不适合存储需要需要经常改变的数据。例如，用LDAP服务器来存储电话号码是一个很好的选择，但是它不能作为电子商务站点的数据库服务器。<br><br>如果下面每一个问题的答案都是&#8220;是&#8221;，那么把数据存在LDAP中就是一个好主意。<br><br>l 需要在任何平台上都能读取数据吗？<br><br>l 每一个单独的记录项是不是每一天都只有很少的改变？<br><br>l 可以把数据存在平面数据库（flat database）而不是关系型数据库中吗？换句话来说，也就是不管什么范式不范式的，把所有东西都存在一个记录中（差不多只要满足第一范式）。<br><br>最后一个问题可能会唬住一些人，其实用平面数据库去存储一些关系型的数据也是很一般的。例如，一条公司员工的记录就可以包含经理的登录名。用LDAP来存储这类信息是很方便的。一个简单的判断方法：如果可以把保数据存在一张张的卡片里，就可以很容易地把它存在LDAP目录里。<br><br>安全和访问控制<br><br>LDAP提供很复杂的不同层次的访问控制或者ACI。因这些访问可以在服务器端控制，这比用客户端的软件保证数据的安全可安全多了。<br><br>用LDAP的ACI，可以完成：<br><br>l 给予用户改变他们自己的电话号码和家庭地址的权限，但是限制他们对其它数据（如，职务名称，经理的登录名，等等）只有&#8220;只读&#8221;权限。<br><br>l 给予&#8220;HR-admins"组中的所有人权限以改变下面这些用户的信息：经理、工作名称、员工号、部门名称和部门号。但是对其它域没有写权限。<br><br>l 禁止任何人查询LDAP服务器上的用户口令，但是可以允许用户改变他或她自己的口令。<br><br>l 给予经理访问他们上级的家庭电话的只读权限，但是禁止其他人有这个权限。<br><br>l 给予&#8220;host-admins"组中的任何人创建、删除和编辑所有保存在LDAP服务器中的与计算机主机有关的信息<br><br>l 通过Web，允许&#8220;foobar-sales"组中的成员有选择地给予或禁止他们自己读取一部分客户联系数据的读权限。这将允许他们把客户联系信息下载到本地的笔记本电脑或个人数字助理（PDA）上。（如果销售人员的软件都支持LDAP，这将非常有用）<br><br>l 通过Web，允许组的所有者删除或添加他们拥有的组的成员。例如：可以允许销售经理给予或禁止销售人员改变Web页的权限。也可以允许邮件假名（mail aliase）的所有者不经过IT技术人员就直接从邮件假名中删除或添加用户。&#8220;公用&#8221;的邮件列表应该允许用户从邮件假名中添加或删除自己（但是只能是自己）。也可以对IP地址或主机名加以限制。例如，某些域只允许用户IP地址以192.168.200.*开头的有读的权限，或者用户反向查找DNS得到的主机名必须为*.foobar.com。<br><br>LDAP目录树的结构<br><br>LDAP目录以树状的层次结构来存储数据。如果你对自顶向下的DNS树或UNIX文件的目录树比较熟悉，也就很容易掌握LDAP目录树这个概念了。就象DNS的主机名那样，LDAP目录记录的标识名（Distinguished Name，简称DN）是用来读取单个记录，以及回溯到树的顶部。后面会做详细地介绍。<br><br>为什么要用层次结构来组织数据呢？原因是多方面的。下面是可能遇到的一些情况：<br><br>l 如果你想把所有的美国客户的联系信息都&#8220;推&#8221;到位于到西雅图办公室（负责营销）的LDAP服务器上，但是你不想把公司的资产管理信息&#8220;推&#8221;到那里。<br><br>l 你可能想根据目录树的结构给予不同的员工组不同的权限。在下面的例子里，资产管理组对&#8220;asset-mgmt"部分有完全的访问权限，但是不能访问其它地方。<br><br>l 把LDAP存储和复制功能结合起来，可以定制目录树的结构以降低对WAN带宽的要求。位于西雅图的营销办公室需要每分钟更新的美国销售状况的信息，但是欧洲的销售情况就只要每小时更新一次就行了。<br><br>刨根问底：基准DN<br><br>LDAP目录树的最顶部就是根，也就是所谓的&#8220;基准DN"。基准DN通常使用下面列出的三种格式之一。假定我在名为FooBar的电子商务公司工作，这家公司在Internet上的名字是foobar.com。<br><br>o="FooBar, Inc.", c=US<br><br>（以X.500格式表示的基准DN）<br><br>在这个例子中，o=FooBar, Inc. 表示组织名，在这里就是公司名的同义词。c=US 表示公司的总部在美国。以前，一般都用这种方式来表示基准DN。但是事物总是在不断变化的，现在所有的公司都已经（或计划）上Internet上。随着 Internet的全球化，在基准DN中使用国家代码很容易让人产生混淆。现在，X.500格式发展成下面列出的两种格式。<br><br>o=foobar.com<br><br>（用公司的Internet地址表示的基准DN）<br><br>这种格式很直观，用公司的域名作为基准DN。这也是现在最常用的格式。<br><br>dc=foobar, dc=com<br><br>（用DNS域名的不同部分组成的基准DN）<br><br>就象上面那一种格式，这种格式也是以DNS域名为基础的，但是上面那种格式不改变域名（也就更易读），而这种格式把域名： foobar.com分成两部分 dc=foobar, dc=com。在理论上，这种格式可能会更灵活一点，但是对于最终用户来说也更难记忆一点。考虑一下foobar.com这个例子。当 foobar.com和gizmo.com合并之后，可以简单的把&#8220;dc=com"当作基准DN。把新的记录放到已经存在的dc=gizmo, dc=com目录下，这样就简化了很多工作（当然，如果foobar.com和wocket.edu合并，这个方法就不能用了）。如果LDAP服务器是新安装的，我建议你使用这种格式。再请注意一下，如果你打算使用活动目录（Actrive Directory），Microsoft已经限制你必须使用这种格式。<br><br>更上一层楼：在目录树中怎么组织数据<br><br>在UNIX文件系统中，最顶层是根目录（root）。在根目录的下面有很多的文件和目录。象上面介绍的那样，LDAP目录也是用同样的方法组织起来的。<br><br>在根目录下，要把数据从逻辑上区分开。因为历史上（X.500）的原因，大多数LDAP目录用OU从逻辑上把数据分开来。OU表示 &#8220;Organization Unit"，在X.500协议中是用来表示公司内部的机构：销售部、财务部，等等。现在LDAP还保留ou=这样的命名规则，但是扩展了分类的范围，可以分类为：ou=people, ou=groups, ou=devices，等等。更低一级的OU有时用来做更细的归类。例如：LDAP目录树（不包括单独的记录）可能会是这样的：<br><br>dc=foobar, dc=com<br><br>ou=customers<br><br>ou=asia<br><br>ou=europe<br><br>ou=usa<br><br>ou=employees<br><br>ou=rooms<br><br>ou=groups<br><br>ou=assets-mgmt<br><br>ou=nisgroups<br><br>ou=recipes<br><br>单独的LDAP记录<br><br>DN是LDAP记录项的名字<br><br>在LDAP目录中的所有记录项都有一个唯一的&#8220;Distinguished Name"，也就是DN。每一个LDAP记录项的DN是由两个部分组成的：相对DN（RDN）和记录在LDAP目录中的位置。<br><br>RDN是DN中与目录树的结构无关的部分。在LDAP目录中存储的记录项都要有一个名字，这个名字通常存在cn（Common Name）这个属性里。因为几乎所有的东西都有一个名字，在LDAP中存储的对象都用它们的cn值作为RDN的基础。如果我把最喜欢的吃燕麦粥食谱存为一个记录，我就会用cn=Oatmeal Deluxe作为记录项的RDN。<br><br>l 我的LDAP目录的基准DN是dc=foobar,dc=com<br><br>l 我把自己的食谱作为LDAP的记录项存在ou=recipes<br><br>l 我的LDAP记录项的RDN设为cn=Oatmeal Deluxe<br><br>上面这些构成了燕麦粥食谱的LDAP记录的完整DN。记住，DN的读法和DNS主机名类似。下面就是完整的DN：<br><br>cn=Oatmeal Deluxe,ou=recipes,dc=foobar,dc=com<br><br>举一个实际的例子来说明DN<br><br>现在为公司的员工设置一个DN。可以用基于cn或uid（User ID），作为典型的用户帐号。例如，FooBar的员工Fran Smith（登录名：fsmith）的DN可以为下面两种格式：<br><br>uid=fsmith,ou=employees,dc=foobar,dc=com<br><br>（基于登录名）<br><br>LDAP（以及X.500）用uid表示&#8220;User ID"，不要把它和UNIX的uid号混淆了。大多数公司都会给每一个员工唯一的登录名，因此用这个办法可以很好地保存员工的信息。你不用担心以后还会有一个叫Fran Smith的加入公司，如果Fran改变了她的名字（结婚？离婚？或宗教原因？），也用不着改变LDAP记录项的DN。<br><br>cn=Fran Smith,ou=employees,dc=foobar,dc=com<br><br>（基于姓名）<br><br>可以看到这种格式使用了Common Name（CN）。可以把Common Name当成一个人的全名。这种格式有一个很明显的缺点就是：如果名字改变了，LDAP的记录就要从一个DN转移到另一个DN。但是，我们应该尽可能地避免改变一个记录项的DN。<br><br>定制目录的对象类型<br><br>你可以用LDAP存储各种类型的数据对象，只要这些对象可以用属性来表示，下面这些是可以在LDAP中存储的一些信息：<br><br>l 员工信息：员工的姓名、登录名、口令、员工号、他的经理的登录名，邮件服务器，等等。<br><br>l 物品跟踪信息：计算机名、IP地址、标签、型号、所在位置，等等。<br><br>l 客户联系列表：客户的公司名、主要联系人的电话、传真和电子邮件，等等。<br><br>l 会议厅信息：会议厅的名字、位置、可以坐多少人、电话号码、是否有投影机。<br><br>l 食谱信息：菜的名字、配料、烹调方法以及准备方法。<br><br>因为LDAP目录可以定制成存储任何文本或二进制数据，到底存什么要由你自己决定。LDAP目录用对象类型（object classes）的概念来定义运行哪一类的对象使用什么属性。在几乎所有的LDAP服务器中，你都要根据自己的需要扩展基本的LDAP目录的功能，创建新的对象类型或者扩展现存的对象类型。<br><br>LDAP目录以一系列&#8220;属性对&#8221;的形式来存储记录项，每一个记录项包括属性类型和属性值（这与关系型数据库用行和列来存取数据有根本的不同）。下面是我存在LDAP目录中的一部分食谱记录：<br><br>dn: cn=Oatmeal Deluxe, ou=recipes, dc=foobar, dc=com<br><br>cn: Instant Oatmeal Deluxe<br><br>recipeCuisine: breakfast<br><br>recipeIngredient: 1 packet instant oatmeal<br><br>recipeIngredient: 1 cup water<br><br>recipeIngredient: 1 pinch salt<br><br>recipeIngredient: 1 tsp brown sugar<br><br>recipeIngredient: 1/4 apple, any type<br><br>请注意上面每一种配料都作为属性recipeIngredient值。LDAP目录被设计成象上面那样为一个属性保存多个值的，而不是在每一个属性的后面用逗号把一系列值分开。<br><br>因为用这样的方式存储数据，所以数据库就有很大的灵活性，不必为加入一些新的数据就重新创建表和索引。更重要的是，LDAP目录不必花费内存或硬盘空间处理&#8220;空&#8221;域，也就是说，实际上不使用可选择的域也不会花费你任何资源。<br><br>作为例子的一个单独的数据项<br><br>让我们看看下面这个例子。我们用Foobar, Inc.的员工Fran Smith的LDAP记录。这个记录项的格式是LDIF，用来导入和导出LDAP目录的记录项。<br><br>dn: uid=fsmith, ou=employees, dc=foobar, dc=com<br><br>objectclass: person<br><br>objectclass: organizationalPerson<br><br>objectclass: inetOrgPerson<br><br>objectclass: foobarPerson<br><br>uid: fsmith<br><br>givenname: Fran<br><br>sn: Smith<br><br>cn: Fran Smith<br><br>cn: Frances Smith<br><br>telephonenumber: 510-555-1234<br><br>roomnumber: 122G<br><br>o: Foobar, Inc.<br><br>mailRoutingAddress: <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#102;&#115;&#109;&#105;&#116;&#104;&#64;&#102;&#111;&#111;&#98;&#97;&#114;&#46;&#99;&#111;&#109;"><u><font color=#0000ff>fsmith@foobar.com</font></u></a>mailhost: mail.foobar.com<br><br>userpassword: 3x1231v76T89N<br><br>uidnumber: 1234<br><br>gidnumber: 1200<br><br>homedirectory: /home/fsmith<br><br>loginshell: /usr/local/bin/bash<br><br>属性的值在保存的时候是保留大小写的，但是在默认情况下搜索的时候是不区分大小写的。某些特殊的属性（例如，password）在搜索的时候需要区分大小写。<br><br>让我们一点一点地分析上面的记录项。<br><br>dn: uid=fsmith, ou=employees, dc=foobar, dc=com<br><br>这是Fran的LDAP记录项的完整DN，包括在目录树中的完整路径。LDAP（和X.500）使用uid（User ID），不要把它和UNIX的uid号混淆了。<br><br>objectclass: person<br><br>objectclass: organizationalPerson<br><br>objectclass: inetOrgPerson<br><br>objectclass: foobarPerson<br><br>可以为任何一个对象根据需要分配多个对象类型。person对象类型要求cn（common name）和sn（surname）这两个域不能为空。persion对象类型允许有其它的可选域，包括givenname、 telephonenumber，等等。organizational Person给person加入更多的可选域，inetOrgPerson又加入更多的可选域（包括电子邮件信息）。最后，foobarPerson是为 Foobar定制的对象类型，加入了很多定制的属性。<br><br>uid: fsmith<br><br>givenname: Fran<br><br>sn: Smith<br><br>cn: Fran Smith<br><br>cn: Frances Smith<br><br>telephonenumber: 510-555-1234<br><br>roomnumber: 122G<br><br>o: Foobar, Inc.<br><br>以前说过了，uid表示User ID。当看到uid的时候，就在脑袋里想一想&#8220;login"。<br><br>请注意CN有多个值。就象上面介绍的，LDAP允许某些属性有多个值。为什么允许有多个值呢？假定你在用公司的LDAP服务器查找Fran 的电话号码。你可能只知道她的名字叫Fran，但是对人力资源处的人来说她的正式名字叫做Frances。因为保存了她的两个名字，所以用任何一个名字检索都可以找到Fran的电话号码、电子邮件和办公房间号，等等。<br><br>mailRoutingAddress: <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#102;&#115;&#109;&#105;&#116;&#104;&#64;&#102;&#111;&#111;&#98;&#97;&#114;&#46;&#99;&#111;&#109;"><u><font color=#0000ff>fsmith@foobar.com</font></u></a>mailhost: mail.foobar.com<br><br>就象现在大多数的公司都上网了，Foobar用Sendmail发送邮件和处理外部邮件路由信息。Foobar把所有用户的邮件信息都存在LDAP中。最新版本的Sendmail支持这项功能。<br><br>Userpassword: 3x1231v76T89N<br><br>uidnumber: 1234<br><br>gidnumber: 1200<br><br>gecos: Frances Smith<br><br>homedirectory: /home/fsmith<br><br>loginshell: /usr/local/bin/bash<br><br>注意，Foobar的系统管理员把所有用户的口令映射信息也都存在LDAP中。FoobarPerson类型的对象具有这种能力。再注意一下，用户口令是用UNIX的口令加密格式存储的。UNIX的uid在这里为uidnumber。提醒你一下，关于如何在LDAP中保存NIS信息，有完整的一份RFC。在以后的文章中我会谈一谈NIS的集成。<br><br>LDAP复制<br><br>LDAP服务器可以使用基于&#8220;推&#8221;或者&#8220;拉&#8221;的技术，用简单或基于安全证书的安全验证，复制一部分或者所有的数据。<br><br>例如，Foobar有一个&#8220;公用的&#8221;LDAP服务器，地址为ldap.foobar.com，端口为389。Netscape Communicator的电子邮件查询功能、UNIX的&#8220;ph"命令要用到这个服务器，用户也可以在任何地方查询这个服务器上的员工和客户联系信息。公司的主LDAP服务器运行在相同的计算机上，不过端口号是1389。<br><br>你可能即不想让员工查询资产管理或食谱的信息，又不想让信息技术人员看到整个公司的LDAP目录。为了解决这个问题，Foobar有选择地把子目录树从主LDAP服务器复制到&#8220;公用&#8221;LDAP服务器上，不复制需要隐藏的信息。为了保持数据始终是最新的，主目录服务器被设置成即时&#8220;推&#8221;同步。这些种方法主要是为了方便，而不是安全，因为如果有权限的用户想查询所有的数据，可以用另一个LDAP端口。<br><br>假定Foobar通过从奥克兰到欧洲的低带宽数据的连接用LDAP管理客户联系信息。可以建立从ldap.foobar.com:1389到munich-ldap.foobar.com:389的数据复制，象下面这样：<br><br>periodic pull: ou=asia,ou=customers,o=sendmail.com<br><br>periodic pull: ou=us,ou=customers,o=sendmail.com<br><br>immediate push: ou=europe,ou=customers,o=sendmail.com<br><br>&#8220;拉&#8221;连接每15分钟同步一次，在上面假定的情况下足够了。&#8220;推&#8221;连接保证任何欧洲的联系信息发生了变化就立即被&#8220;推&#8221;到Munich。<br><br>用上面的复制模式，用户为了访问数据需要连接到哪一台服务器呢？在Munich的用户可以简单地连接到本地服务器。如果他们改变了数据，本地的LDAP服务器就会把这些变化传到主LDAP服务器。然后，主LDAP服务器把这些变化&#8220;推&#8221;回本地的&#8220;公用&#8221;LDAP服务器保持数据的同步。这对本地的用户有很大的好处，因为所有的查询（大多数是读）都在本地的服务器上进行，速度非常快。当需要改变信息的时候，最终用户不需要重新配置客户端的软件，因为LDAP目录服务器为他们完成了所有的数据交换工作。 
<img src ="http://www.blogjava.net/junky/aggbug/118852.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-21 13:09 <a href="http://www.blogjava.net/junky/archive/2007/05/21/118852.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>手把手教你可复用SSO组件的设计（原理篇）(转)</title><link>http://www.blogjava.net/junky/archive/2007/05/21/118798.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Mon, 21 May 2007 01:33:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/21/118798.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/118798.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/21/118798.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/118798.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/118798.html</trackback:ping><description><![CDATA[在结构设计上复用性是一个很重要的特征，昨天半夜我发的系统地非侵入性也是很重要的，有同志邀我看看他的SSO系统，不过看后都我觉得不甚满意，如果要服用的话需要把分散的代码一点点抠出来，然后经过反复的修改调试后才能在新的系统中使用，那位老兄的SSO系统功能可能确实强大，而且还用了新技术，不过在复用性上我看还是没有摆脱集成上的痛苦，作过系统集成的同学们肯定对此深有感触。
<p>昨天才批判了很多同学写东西语焉不详，结果回头就自己给了自己一耳巴子，上几篇关于SSO的描述都不够详细，于是这里在手把手系列里我们来一起看看如何设计一个高度可服用的SSO模块，这里我们假设所有的站点都使用.NET，因为成熟的SSO需要和采用各种不同技术的站点之间实现SSO，于是有JAVA,PHP,COM+,.NET多种形式的PSO模块。这里我们精力有限，所以先假设都用.NET，如果你会JAVA也可以自己用java来实现PSO。</p>
<p>一般来说单点认证都需要两端来完成，在认证中心端的我们称之为SSO，在网站端的模块我们称之为PSO。两个模块之间采用二次重定向技术来实现同步两端票据的方式来实现单点登陆</p>
<p>首先我们就来看看这两个模块式如何配合来完成单点认证的</p>
<p>第一个场景是用户首先访问认证中心登陆再去进入成员网站的情况：<br><br><a href="http://www.cnblogs.com/images/cnblogs_com/Alexander-Lee/WindowsLiveWriter/SSO_D130/image%7B0%7D%5B3%5D.png" atomicselection="true"><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=388 src="http://www.cnblogs.com/images/cnblogs_com/Alexander-Lee/WindowsLiveWriter/SSO_D130/image%7B0%7D_thumb%5B1%5D.png" width=571 border=0></a></p>
<p>首先是登陆后产生一个SSO的票据，这个票据是最重要的，因为它是决定用户是否登陆的关键。这个票据可以是Cookie,也可以是Session,我比较倾向于Cookie，因为现在有3DES加密，加密后篡改Cookie几乎成为不可能，所以无论是对于服务器负担来说还是安全性都是Cookie比较好，可能人认为万一不支持Cookie呢，不过我想Demo应该没问题吧，大不了我设计成两个都支持：}，<br></p>
<p>PS，为什么不用非对称加密？其实那个效率不高，3DES的安全性已经足够了，至少现在还没有人宣称能破解<br><br>在登陆后就可以通知用户你已经登陆了，现在你可以去访问成员站点了，这个时候用户点击了成员站点的URL，进去了，这个时候首先就需要接受PSO组件的盘查，你有没有PSO的票据呢？很显然是没有的，所以这个时候请求就被Redirect回了认证中心，认证中心检查用户已经有了SSO的票据了，认为用户已经登录了，就把用户的SSO票据附加在URL后边然后Redirect回成员站点，成员站点的PSO这个时候获取到了SSO票据，于是知道了用户已经在认证端登录了，于是就创建一个PSO票据，然后返回给用户他所请求的内容。所以我们来看看其实PSO的逻辑更加复杂一点。</p>
<p>SSO的逻辑：</p>
<p><a href="http://www.cnblogs.com/images/cnblogs_com/Alexander-Lee/WindowsLiveWriter/SSO_D130/image%7B0%7D%5B8%5D.png" atomicselection="true"><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=222 src="http://www.cnblogs.com/images/cnblogs_com/Alexander-Lee/WindowsLiveWriter/SSO_D130/image%7B0%7D%5B7%5D.png" width=240 border=0></a> </p>
<p>PSO的逻辑：</p>
<p><a href="http://www.cnblogs.com/images/cnblogs_com/Alexander-Lee/WindowsLiveWriter/SSO_D130/image%7B0%7D%5B6%5D.png" atomicselection="true"><img style="BORDER-RIGHT: 0px; BORDER-TOP: 0px; BORDER-LEFT: 0px; BORDER-BOTTOM: 0px" height=345 src="http://www.cnblogs.com/images/cnblogs_com/Alexander-Lee/WindowsLiveWriter/SSO_D130/image%7B0%7D_thumb%5B2%5D.png" width=217 border=0></a> </p>
<p>这里我们可以看到其实两个模块的功能都不算复杂，这里存在几个现实的问题，第一个是加密问题，票据需要加密，传输的URL也需要加密。</p>
<p>还有一个就是上一次我上篇文章里说的，在SSO把票据通过URL发送给PSO的时候，如果我们能够截获这个URL，不管他加没有加密，在下一次我们直接用这个URL去访问站点的时候因为已经包含SSO票据了，所以PSO会认为已经登陆了而直接产生PSO票据然后就让用户进去了，这显然是一个漏洞。所以呢，我们需要在这里给这个URL加一点盐值（所谓盐值其实就是加点料），我们通过在URL里加入时间戳来让这个URL具备时间限制，这个样子URL具备失效期，过了这个时间即使截获到了这个URL也完全没有作用了。</p>
<img src ="http://www.blogjava.net/junky/aggbug/118798.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-21 09:33 <a href="http://www.blogjava.net/junky/archive/2007/05/21/118798.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于单点认证的一个简单实现(结合Form认证)(转)</title><link>http://www.blogjava.net/junky/archive/2007/05/21/118795.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Mon, 21 May 2007 01:32:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/21/118795.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/118795.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/21/118795.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/118795.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/118795.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 最近在做一个无敌长的项目,从五一休假做到十一休假!有一个需求就是要求单点登陆(SSO)解决思路如下: 请求认证的网站 :用一个HttpModule 截取所有请求,判断HttpContext.User是不是Null,如为空,判断Url上是不是有签名过的认证信息, 如果有,判断签名信息是否有效,如果有效,将认证信息写入Cookie中.认证完成认证的网站: 如果登陆页Url中有要求认证的网址,判断用户是...&nbsp;&nbsp;<a href='http://www.blogjava.net/junky/archive/2007/05/21/118795.html'>阅读全文</a><img src ="http://www.blogjava.net/junky/aggbug/118795.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-21 09:32 <a href="http://www.blogjava.net/junky/archive/2007/05/21/118795.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是安全套接字层技术(转)</title><link>http://www.blogjava.net/junky/archive/2007/05/21/118791.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Mon, 21 May 2007 01:00:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/21/118791.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/118791.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/21/118791.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/118791.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/118791.html</trackback:ping><description><![CDATA[<p>安装及配置SSL支持 </p>
<p align=left><strong><font size=4>什么是安全套接字层技术？</font></strong></p>
<p align=left>安全套接字层(SSL)是允许Web浏览器和Web服务通过安全连接进行通信的技术。在这种安全连接上，数据在发送前经过加密码，然后在接收时先解密再进行处理。浏览器和服务器在发送任何数据之前都对所有流量加密。SSL是针对下列重要的安全性考虑的：</p>
<p align=left term="">身份验证</p>
<p align=left>在第一次尝试与Web服务器通过安全连接通信时，这台服务器将以服务器证书的形式向Web浏览器发送一份凭证。这个证书的目的是验证这个站点就是它所宣称的那一个。在有些情况下，服务器可能要求客户端提供证明它就是它所宣称的身份的证书(即客户端身份验证)。</p>
<p align=left term="">机密性</p>
<p align=left>数据通过网络在客户端与服务器之间传送时，第三方可以查看并截获该数据。SSL响应是加密的，这样数据就不会被第三方解密而仍然保持其机密机。</p>
<p align=left term="">完整性</p>
<p align=left>当数据通过网络在客户端与服务器之间传送时，第三方可以查看并截获该数据。SSL帮助保证数据在传输过程中不被第三方修改。 </p>
<p align=left>要在自己的独立Web服务器上安装并配置SSL，需要下列组件。以下各节专门讨论在Tomcat上启用SSL支持。如果使用不同的Web服务器，请参照相应产品的文档。</p>
<p align=left>&#183;&nbsp; Java 安全套接字扩展(Java Secure Socket Extension JSSE))(见<a href="http://java.sun.com/webservices/docs/1.1/tutorial/doc/WebAppSecurity5.html#wp88839" target=_blank><u><font color=#0000ff>使用JSSE</font></u></a>)。</p>
<p align=left>&#183;&nbsp; 服务器证书keystore(见<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp80737"><u><font color=#0000ff>设置数字证书</font></u></a>)。</p>
<p align=left>&#183;&nbsp; 一个HTTPS连接器(见<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp80838"><u><font color=#0000ff>配置SSL连接器</font></u></a>)。</p>
<p align=left>要验证启用了SSL支持，参见<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp86714"><u><font color=#0000ff>验证SSL支持</font></u></a>。</p>
<p align=left>&nbsp;</p>
<p align=left><strong><font size=5>使用JSSE</font></strong></p>
<p align=left>如果使用J2SE SDK v1.3.1，那么需要安装Java安全套接字扩展(JSSE)以使用SSL。JSSE是J2SE 1.4 SDK的一部分。JSSE是一组Java包，它使安全Internet通信成为可能。这些包实现了Java版本的SSL(安全套接字层)和TLS(传输层安全性)协议并包括数据加密、服务器身份验证、消息完整性和可选的客户端验证功能。使用JSSE，开发者在可以为客户端和运行任何TCP/IP之上的应用协议(如HTTP、Telnet、NNTP和FTP)的服务器之间提供安全数据传输。</p>
<p align=left>在默认情况下，jsse.jar文件的位置是&lt;<em>JAVA_HOME</em>&gt;/jre/lib/jsse.jar。有关JSSE的更多信息，参见其Web站点<a href="http://java.sun.com/products/jsse/" target=_blank><u><font color=#0000ff>http://java.sun.com/products/jsse/</font></u></a>。</p>
<p align=left><strong><font size=4>设置数据证书</font></strong></p>
<p align=left>为了实现SSL，一个Web服务必须对每一个接受安全连接的外部接口或者IP地址有一个相关联的证书。这种设计背后的理论是服务器应该对其拥有者身份提供某种合理的保证，特别是在接收任何敏感信息之前。将证书想像为Internet地址的&#8220;数字驱动的许可许&#8221;可能会有帮助。它声明这个站点与哪个公司相关联，以及站点所有者或者管理员的一些基本联系信息。</p>
<p align=left>数字证书是由其拥有者加密签名，任何其他人都难以仿造。对于涉及电子商务的站点，或者任何其他身份验证起重要作用的业务交易站点，可以从像verisign或者Thawte这样的著名证书颁发机构(Certificate Authority CA)购买证书。</p>
<p align=left>如果身份验证并不很重要，比如管理员只是希望保证服务器发送和接收的数据是私有的并且不能被连接中的任何窃听者探听到，则可以只是使用自签名的证书，从而省去获取CA证书的时间和成本。</p>
<p align=left>SSL使用<em>公钥加密</em>，它基于<em>密钥对</em>。密钥对包含一个公钥和一个私钥。如果数据是用其中一把钥匙加密的，那么只能用钥匙对中的另一把钥匙解密。这种特性是建立交易信任和私有性的基础。例如，使用SSL时，服务器计算出一个值并用其私钥加密这个值。加密的值称为<em>数字签名</em>。客户端用服务器的公钥解密这个加密的值并将这个值与它自己的计算值进行比较。如果这两个值相匹配，那么客户端就可以信任签名是真的，因为只有用私钥才可以生成这个签名。</p>
<p align=left>数字证书与HTTPS协议一共使用以验证Web客户端身份。除非安装了数字证书，否则大多数Web服务的HTTPS服务都不会运行。使用下面描述的过程设置可以被Web服务器使用的数字证书以启用SSL。</p>
<p align=left>一种可以用于设置数字证书的工具是keytool，这是一种与J2SE 1.4 SDK一同发布的密钥和证书管理工具。它使用户可以管理他们自己的公/私钥对以及相关的证书，以在自验证(用户向其他用户/服务验证他/她自己)或者数字完整性和身份验证服务用使用。它还让用户可以缓存他们通信伙伴的公钥(以证书的形式)。要更好地理解公钥加密，参阅以下地址上的文档</p>
<p align=left><a href="http://java.sun.com/webservices/docs/1.1/tutorial/doc/%20http:/java.sun.com/j2se/1.4.1/docs/tooldocs/solaris/keytool.html" target=_blank><u><font color=#0000ff>http://java.sun.com/j2se/1.4.1/docs/tooldocs/solaris/keytool.html</font></u></a> </p>
<p align=left>证书是来自某一实体(人员、公司等)的数字签名的声明，表明其他实体的公钥(或者一些其他信息)有一个特定值。当对数据进行数字签名后，可以对该签名进行验证以检查数字完整性和真实性。<em>完整性</em>意味着数据没有被修改或者篡改，<em>真实性</em>意味着数据确实是来自宣称创建和对它签名的地方。</p>
<p align=left>keytool将储存密钥和证书储存在一个名为<em>keystore</em>的文件中。Keystore的默认实现将keystore实现为一个文件。它用密码保护私钥。有关keytool的更多信息，参阅以下地址的文档</p>
<p align=left><a href="http://java.sun.com/j2se/1.4.1/docs/tooldocs/solaris/keytool.html" target=_blank><u><font color=#0000ff>http://java.sun.com/j2se/1.4.1/docs/tooldocs/solaris/keytool.html</font></u></a> </p>
<p align=left>本节描述创建一个名为server.keystore的服务器keystore以及名为client.keystore的客户端keystore。这两个文件构成一个密钥对。这些文件通常是在&lt;<em>HOME</em>&gt;目录中或者在应用程序目录中创建的。</p>
<p align=left>除了服务器和客户端keystore，还必须有签名的证书，它必须存在于服务器上。在服务器验证客户端身份时，这个文件必须包含证书颁发机构的公钥证书或者客户端的公钥证书。我们将在&lt;<em>HOME</em>&gt;目录中创建server.cer。</p>
<p align=left>通常，keystore文件是由密码保护的。对于server.keystore、client.keystore和server.cer文件这个密码的默认值是changeit。</p>
<p align=left>我们使用keytool工具创建keystore文件。可以在&lt;<em>JAVA_HOME</em>&gt;/bin目录中找到keytool工具。</p>
<p align=left>要设置数字证书，</p>
<p align=left>1.&nbsp;&nbsp;&nbsp;&nbsp; 生成密钥对</p>
<p align=left relative="">keytool工具使您可以生成密钥对。随J2SE SDK发布的keytool工具在程序中增加了实现了RSA算法的Java 密码扩展提供程序。这个提供程序使您可以导入RSA签名的证书。</p>
<p align=left relative="">如下运行keytool工具以生成keystore文件，用keystore文件的名字替换&lt;<em>keystore_filename</em>&gt;，例如server.keystore。如果使用Tomcat服务器，文件必须命名为.keystore并位于Tomcat运行的计算机的home目录中，或者通过在Tomcat配置文件中的&lt;Factory&gt;元素中添加一个keystoreFile属性、或者在admintool的Connector (8443)节点上指定该文件的位置以告诉Tomcat这个keystore文件是什么地方。</p>
<blockquote>
<blockquote>
<p align=left>keytool -genkey -keyalg RSA -alias tomcat-server<br>&nbsp;&nbsp;&nbsp; -keystore &lt;<em>keystore_filename</em>&gt;</p>
</blockquote></blockquote>
<p align=left>2.&nbsp;&nbsp;&nbsp;&nbsp; keytool工具提示您加入以下信息：</p>
<p align=left>a.&nbsp;&nbsp; Keystore password--输入默认密码，即changeit。有关改变密码的信息参见keytool文档。</p>
<p align=left>b.&nbsp;&nbsp; First and last name——输入适当的值，例如JWSDP。</p>
<p align=left>c.&nbsp;&nbsp; Organizational unit——输入适当的值，例如Java Web Services。</p>
<p align=left>d.&nbsp;&nbsp; Organization——输入适当的值，如Sun Microsystems。</p>
<p align=left>e.&nbsp;&nbsp; City or locality——输入适当的值，例如Santa Clara。</p>
<p align=left>f.&nbsp;&nbsp;&nbsp; State or province——.输入非缩写名，如CA。</p>
<p align=left>g.&nbsp;&nbsp; Two-letter country code-——对于美国，双字母国家代码是US。</p>
<p align=left>h.&nbsp;&nbsp; Review the information you've entered so far, enter Yes if it is correct.检查输入的信息，如果都正确，则输入Yes。</p>
<p align=left>i.&nbsp;&nbsp;&nbsp; Key password for the Web server——不输入密码。按回车。</p>
<p align=left>下一步是为该keystore生成签名的证书。一个自签名的证书对于大多数SSL通信来说是可以接受的。如果使用自签名的证书，则进入<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp80779"><u><font color=#0000ff>创建自签名的证书</font></u></a>。如果想让证书由CA数字签名，则进入<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp80788"><u><font color=#0000ff>获取数字签名的证书</font></u></a>。</p>
<p align=left><strong><font size=3>创建自签名的证书</font></strong></p>
<p align=left>本例假设keystore命名为server.keystore，证书文件是server.cer，CA文件是cacerts.jks。在&lt;<em>HOME</em>&gt;目录中运行以下命令以使它们创建到这里。</p>
<p align=left>1.&nbsp;&nbsp;&nbsp;&nbsp; 将服务器证书导出为证书文件：</p>
<blockquote>
<p align=left relative="relative">keytool -keystore server.keystore -export -alias tomcat-server -file server.cer </p>
</blockquote>
<p align=left>2.&nbsp;&nbsp;&nbsp;&nbsp; 输入密码(changeit)：</p>
<p align=left relative="">Keytool返回下列消息：</p>
<blockquote>
<p align=left relative="relative">Certificate stored in file &lt;server.cer&gt; </p>
</blockquote>
<p align=left>3.&nbsp;&nbsp;&nbsp;&nbsp; 将新的服务器证书导入到证书颁发机构文件cacerts.jks：</p>
<blockquote>
<p align=left relative="relative">keytool -import -alias serverCA -keystore &lt;HOME&gt;/cacerts.jks <br>-file server.cer </p>
</blockquote>
<p align=left>4.&nbsp;&nbsp;&nbsp;&nbsp; 输入密码(changeit)。</p>
<p align=left relative="">Keytool返回类似下面的消息：</p>
<blockquote>
<p align=left relative="relative">Owner: CN=JWSDP, OU=Java Web Services, O=Sun, L=Santa Clara,<br>ST=CA, C=US<br>Issuer: CN=JWSDP, OU=Java Web Services, O=Sun, L=Santa Clara,<br>ST=CA, C=US<br>Serial number: 3e39e3e0<br>Valid from: Thu Jan 30 18:48:00 PST 2003 until: Wed Apr 30 19:48:00 PDT 2003<br>Certificate fingerprints:<br>MD5: 44:89:AF:54:FE:79:66:DB:0D:BE:DC:15:A9:B6:09:84<br>SHA1:21:09:8A:F6:78:E5:C2:19:D5:FF:CB:DB:AB:78:9B:98:8D:06:8C:71<br>Trust this certificate? [no]: yes<br>Certificate was added to keystore </p>
</blockquote>
<p align=left><strong><font size=3>获取数字签名的证书</font></strong></p>
<p align=left>本例假设keystore命名为server.keystore，证书文件为server.cer，并且CA文件是cacerts.jks。</p>
<p align=left>1.&nbsp;&nbsp;&nbsp;&nbsp; CA获得数字签名的证书。为此，</p>
<p align=left>a.&nbsp;&nbsp; 生成证书签名请求(Certificate Signing Request CSR)。</p>
<blockquote>
<p align=left relative="relative">keytool -certreq -alias tomcat-server -keyalg RSA<br>-file &lt;csr_filename&gt; -keystore cacerts.jks </p>
</blockquote>
<p align=left>b.&nbsp;&nbsp; 发送<em>csr_filename</em>的内容以进行签名。</p>
<p align=left>c.&nbsp;&nbsp; 如果使用Verisign CA，则进入<a href="http://digitalid.verisign.com/" target=_blank><u><font color=#0000ff>http://digitalid.verisign.com/</font></u></a>。Verisign将在电子邮件中发送签名的证书。将这个证书储存在一个文件中。</p>
<p align=left>d.&nbsp;&nbsp; 将通过电子邮件收到的签字的证书导入服务器：</p>
<blockquote>
<p align=left relative="relative">keytool -import -alias tomcat-server -trustcacerts -file <br>&lt;signed_cert_file&gt; -keystore &lt;keystore_filename&gt; </p>
</blockquote>
<p align=left>2.&nbsp;&nbsp;&nbsp;&nbsp; 导入证书(如果使用CA签名的证书)。</p>
<p align=left relative="">如果证书是由证书颁发机构(CA)签名，那么必须导入CA证书。如果只使用自签名证书则可跳过这一步。如果使用自签名证书或者由浏览器不识别的CA签名的证书，那么用户第一次试图访问服务器时就会跳出一个对话框。用户可以选择只在这个会话中、还是永远信任这个证书。</p>
<p align=left relative="">要在标准版本的Java 2平台安装CA证书，如下运行keytool工具。</p>
<blockquote>
<p align=left relative="relative">keytool -import -trustcacerts -alias root <br>&nbsp;&nbsp;-file &lt;ca-cert-filename&gt; -keystore &lt;keystore-filename&gt; </p>
</blockquote>
<p align=left><strong><font size=3>为相互验证创建客户端证书</font></strong></p>
<p align=left>创建客户端证书的过程与创建服务器证书类似。</p>
<p align=left>1.&nbsp;&nbsp;&nbsp;&nbsp; 用keytool在所选的keystore文件中创建客户端证书：</p>
<blockquote>
<p align=left relative="relative">keytool -genkey -keyalg RSA -alias jwsdp-client -keystore client.keystore </p>
</blockquote>
<p align=left relative="">会提示输入密码。输入默认密码changeit。根据提示输入名字、组织和其他客户端信息。不要在&#8220;Key password for &lt;client&gt;&#8221;输入任何内容，只需要按回车。</p>
<p align=left>2.&nbsp;&nbsp;&nbsp;&nbsp; 将新客户端证书从keystore导出到证书文件：</p>
<blockquote>
<p align=left relative="relative">keytool -keystore client.keystore -export -alias jwsdp-client -file client.cer </p>
</blockquote>
<p align=left>3.&nbsp;&nbsp;&nbsp;&nbsp; 输入keystore密码(changeit)。Keytool将返回该消息：</p>
<blockquote>
<p align=left>Certificate stored in file &lt;client.cer&gt;</p>
</blockquote>
<p align=left>4.&nbsp;&nbsp;&nbsp;&nbsp; 将新客户端证书导入到服务器的证书颁发机构文件cacerts.jks中。这使服务器在SSL相互验证过程中信任该客户端。</p>
<p align=left relative="">keytool -import -alias root -keystore &lt;HOME&gt;/cacerts.jks <br>-file client.cer </p>
<p align=left>5.&nbsp;&nbsp;&nbsp;&nbsp; 输入keystore密码(changeit)。Keytool返回下面的消息：</p>
<blockquote>
<p align=left relative="relative">Owner: CN=JWSDP Client, OU=Java Web Services, O=Sun, L=Santa<br>Clara, ST=CA, C=US<br>Issuer: CN=JWSDP Client, OU=Java Web Services, O=Sun, L=Santa Clara, ST=CA, C=US<br>Serial number: 3e39e66a<br>Valid from: Thu Jan 30 18:58:50 PST 2003 until: Wed Apr 30<br>19:58:50 PDT 2003<br>Certificate fingerprints:<br>MD5: 5A:B0:4C:88:4E:F8:EF:E9:E5:8B:53:BD:D0:AA:8E:5A<br>SHA1:90:00:36:5B:E0:A7:A2:BD:67:DB:EA:37:B9:61:3E:26:B3:89:46:<br>32<br>Trust this certificate? [no]: yes<br>Certificate was added to keystore </p>
</blockquote>
<p align=left><strong><font size=2><em>检查相互验证是否已运行</em></font></strong></p>
<p align=left>要证明SSL握手已进行，关闭Tomcat，在&lt;<em>JWSDP_HOME</em>&gt;/bin/catalina.bat文件中发送debug旗标，然后重新启动Tomcat。服务将显示握手消息，或者将它们写入文件&lt;<em>JWSDP_HOME</em>&gt;/logs/launcher.server.log中。下面例子以<strong>粗体</strong>显示新代码。</p>
<blockquote>
<p align=left>rem Execute the Tomcat launcher<br>"%JAVA_HOME%\bin\java.exe" <strong>-Djavax.net.debug=ssl,handshake</strong><br>-classpath %PRG%\..;%PRG%\..\..\jwsdp-shared\bin;"%PRG%</p>
</blockquote>
<p align=left><strong><font size=3>在Tomcat服务器使用PKCS12证书</font></strong></p>
<p align=left>Java WSDP支持PKCS12格式的证书。PKCS12标准规定储存或者传输用户私钥、证书和各种秘密的可移植格式。更多信息参见以下Web站点：</p>
<blockquote>
<p align=left><a href="http://www.rsasecurity.com/rsalabs/pkcs/pkcs-12" target=_blank><u><font color=#0000ff>http://www.rsasecurity.com/rsalabs/pkcs/pkcs-12</font></u></a></p>
</blockquote>
<p align=left>如果有PKCS12格式的证书，那么必须将它转换为JKS格式。进行转换的命令是：</p>
<blockquote>
<p align=left>keytool -pkcs12 -pkcsFile &lt;<em>fileName</em>&gt; -pkcsKeyStorePass<br>&lt;<em>password</em>&gt; -pkcsKeyPass &lt;<em>password</em>&gt; -jksFile &lt;<em>outputFileName</em>&gt;<br>-jksKeyStorePass &lt;<em>password</em>&gt; <a name=wp81091>&nbsp;</a> </p>
</blockquote>
<p align=left>结果是一个含有密钥——私钥和证书链——的JKS文件。</p>
<p align=left>用带-export选项的keytool将证书导出到一个文件中，如abc.cer：</p>
<blockquote>
<p align=left>keytool -keystore &lt;<em>outputFileName</em>&gt; -export -alias &lt;<em>server</em>&gt; -file abc.cer</p>
</blockquote>
<p align=left><strong><font size=3>证书的各种命令</font></strong></p>
<p align=left>&#183;&nbsp; 检查服务器证书的内容：</p>
<blockquote>
<p align=left>keytool -list -keystore server.keystore -alias tomcat-server -v</p>
</blockquote>
<p align=left>&#183;&nbsp; 检查cacerts文件的内容：</p>
<blockquote>
<p align=left>keytool -list -keystore cacerts.jks</p>
</blockquote>
<p align=left><strong><font size=4>配置SSL连接器</font></strong></p>
<p align=left>取决于Web服务器，可能它会启用SSL HTTPS连接器，也可能不启用。如果使用Tomcat服务器，那么就没有配置SSL连接器。本节描述如何为Tomcat配置SSL HTTPS连接器。如果使用其他Web服务器，则应该参考相应服务器的文档。</p>
<p align=left>在部署描述符中必须包含一个用于SSL连接的元素Connector。在改变服务器部署描述符之前，必须关闭服务器。下列代码是在Web服务器上启用SSL连接器的代码示例：</p>
<blockquote>
<p align=left>&lt;Connector<br>&nbsp;&nbsp;&nbsp;className="org.apache.coyote.tomcat5.CoyoteConnector"<br>&nbsp;&nbsp;&nbsp;port="8443" minProcessors="5" maxProcessors="75"<br>&nbsp;&nbsp;&nbsp;enableLookups="true" acceptCount="10" debug="0"<br>&nbsp;&nbsp;&nbsp;scheme="https" secure="true" useURIValidationHack="false"&gt;<br>&lt;Factory className="com.sun.web.security.SSLSocketFactory"<br>&nbsp;&nbsp;&nbsp;clientAuth="false" protocol="TLS" debug="0" /&gt;<br>&lt;/Connector&gt;</p>
</blockquote>
<p align=left>这个Connector元素的属性在附录<a href="http://java.sun.com/webservices/docs/1.1/tutorial/doc/Admintool.html#wp63663"><u><font color=#0000ff>A</font></u></a> Tomcat服务器管理工具中有更详细的描述。可以使用下面两种方法之一向Tomcat添加SSL HTTPS连接器：</p>
<p align=left>&#183;&nbsp; 使用admintool添加Connector。参见<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp86736"><u><font color=#0000ff>在admintool中添加SSL连接器</font></u></a>。</p>
<p align=left>&#183;&nbsp; 向服务器的部署描述符添加SSL连接器的Connector元素。参见在server.xml中配置SSL。</p>
<p align=left><strong><font size=3>在admintool中添加SSL连接器</font></strong></p>
<p align=left>要用admintool配置SSL连接器，必须首先像在设置数字证书中所描述的那样创建一个keystore。Tomcat会在它所运行的计算机上的主目录中寻找名为.keystore的keystore。确认已经创建了keystore以后，进行以下步骤：</p>
<p align=left>1.&nbsp;&nbsp;&nbsp;&nbsp; 如果Tomcat还没有启动，就启动它。</p>
<p align=left>2.&nbsp;&nbsp;&nbsp;&nbsp; 在Web浏览器中输入http://localhost:8080/admin in a Web browser启动admintool。</p>
<p align=left>3.&nbsp;&nbsp;&nbsp;&nbsp; 输入为admin角色指定的用户名和密码组合。</p>
<p align=left>4.&nbsp;&nbsp;&nbsp;&nbsp; 在左边窗格中选择服务(Java Web Services Developer Pack)。</p>
<p align=left>5.&nbsp;&nbsp;&nbsp;&nbsp; 从右边窗格中的下拉列表中选择Create New Connector。</p>
<p align=left>6.&nbsp;&nbsp;&nbsp;&nbsp; 在Type域，选择HTTPS。</p>
<p align=left>7.&nbsp;&nbsp;&nbsp;&nbsp; 在Port域，输入8443(或者其他所要求的端口)。这会定义Tomcat会监听哪个安全连接的TCP.IP端口号。</p>
<p align=left>8.&nbsp;&nbsp;&nbsp;&nbsp; 如果创建了keystore，其名字不是.keystore，如果keystore不在Tomcat所运行的计算机上的home目录中，或者密码不是默认值changeit，则输入Keystore Name和keystore Password。如果使用了期望值，则可以不填写这些域。</p>
<p align=left relative="">在Unix和Linux系统中home目录是/home/<em>user_name</em>，在Microsoft Windows系统中是C:\Documents and Settings\user_name。</p>
<p align=left>9.&nbsp;&nbsp;&nbsp;&nbsp; 选择Save以为该会话保存新Connector。</p>
<p align=left>10.&nbsp;&nbsp; 选择Commit Changes以将新Connector信息写入文件server.xml，这样下次Tomat启动时它就是可用的了。</p>
<p align=left>要查看和/或者编辑新创建的Connector，展开Service(Java Web Services Developer Pack)节点，并选择Connector(8443)。</p>
<p align=left><strong><font size=3>在server.xml中配置SSL Connector</font></strong></p>
<p align=left>在默认server.xml中包括了一个SSL Connector的示例Connector元素。在默认情况下这个Connector元素是注释掉的。要为Tomcat启用SSL Connector，去掉围绕着SSL Connector元素的注释标记。为此，执行以下步骤。</p>
<p align=left>1.&nbsp;&nbsp;&nbsp;&nbsp; 如果Tomcat正在运行，则关闭它。当Tomcat再次启动时对&lt;<em>JWSDP_HOME</em>&gt;/conf/server.xml文件的修改就可被它使用了。</p>
<p align=left>2.&nbsp;&nbsp;&nbsp;&nbsp; 在文本编辑器中打开文件&lt;<em>JWSDP_HOME</em>&gt;/conf/server.xml。</p>
<p align=left>3.&nbsp;&nbsp;&nbsp;&nbsp; 在文件中找到下代码部分(可以试着搜索SSL Connector)。删除围绕Connector项的注释。要删除的注释标签以粗体表示。</p>
<blockquote>
<p align=left relative="relative">&nbsp;&nbsp;&lt;!-- SSL Connector on Port 8443 --&gt;</p>
<p align=left>&lt;!--<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;Connector<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;className="org.apache.coyote.tomcat4.CoyoteConnector"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;port="8443" minProcessors="5" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;maxProcessors="75"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;enableLookups="false"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;acceptCount="10" <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;connectionTimeout="60000" debug="0"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;scheme="https" secure="true"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;Factory<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;className="org.apache.coyote.tomcat4.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CoyoteServerSocketFactory"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;clientAuth="false" protocol="TLS" /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Connector&gt;<br>&nbsp;&nbsp;--&gt; </p>
</blockquote>
<p align=left>4.&nbsp;&nbsp;&nbsp;&nbsp; 保存并关闭文件。</p>
<p align=left>5.&nbsp;&nbsp;&nbsp;&nbsp; 启动Tomcat。</p>
<p align=left>在<a href="http://java.sun.com/webservices/docs/1.1/tutorial/doc/Admintool.html#wp63796"><u><font color=#0000ff>Tomcat管理工具</font></u></a>文档中对这个Connector元素中的属性有更详细的描述。</p>
<p align=left><strong><font size=4>验证SSL支持</font></strong></p>
<p align=left>为了进行测试，并验证SSL支持已经正确安装，用连接到在服务器部署描述符中定义的端口的URL装载默认的介绍页面：</p>
<blockquote>
<p align=left>https://localhost:8443/</p>
</blockquote>
<p align=left>这个URL中的https表明浏览器应该使用SSL协议。端口8443就是在前面一步中创建SSL Connector的端口</p>
<p align=left>用户第一次装载这个应用程序时，会显示New Site Certificate对话框。选择Next通过一系列New Site Certificate对话框，到达最后一个对话框时，选择Finish。</p>
<p align=left><strong><font size=4>有关运行SSL的一般性提示</font></strong></p>
<p align=left>SSL协议设计为尽可能地高效和安全。不过，从性能的角度看，加密/解密是一种需要大量计算的过程。不一定要将整个Web应用都放到SSL上，并且一般由开发者决定哪一页面需要安全连接，哪一页面不需要。可能需要安全连接的页面包括登录页面、个人信息页面、购物小车付款、或者任何其他可能传送信用卡号的页面。可以仅通过加在地址上用https个取代http前缀而使应用程序中的任何一页面运行在安全套接字上。对任何绝对要求安全连接的页面都应该检查与该页面请求相关联的协议类型，如果没有指定https就要采取相应措施。</p>
<p align=left>使用在安全连接上的基于名字的虚拟主机可能会有问题。这是SSL协议本身的一个设计局限。SSL握手必须在访问HTTP请求之前发生，在握手时客户端浏览器接受服务器证书。结果，不能在身份验证之前确定包含虚拟主机名的请求信息，并且因此不可能对一个IP地址指定多个证书。如果在一个IP地址上的所有虚拟主机需要针对同一个证书进行验证，那么额外的多个虚拟主机就不应该介入服务器上的普通SSL操作。不过要知道，大多数客户端浏览器会比较服务器的域名和在证书中列出的域名，如果有列出的话(主要适用于正式的、CA签名的证书)。如果域名不匹配，那么这些浏览器将向客户端显示一个警告。一般来说，在生产环境中只有基于地址的虚拟主机通常会使用SSL。</p>
<p align=left><strong><font size=3>SSL连接的故障排除</font></strong></p>
<p align=left><strong><font size=2><em>启动</em><em>Tomcat</em><em>时，得到这样的异常&#8220;</em><em>"java.io.FileNotFoundException: {some-directory}/{some-file} not found</em><em>&#8221;</em></font></strong></p>
<p align=left>一个可能的解释是Tomcat不能找到它要寻找的keystore文件。在默认情况下，Tomcat期待keystore命名为.keystore，并且位于Tomcat所在的系统中的home目录(与您自己的计算机上的home目录可能一致、也可能不一致) 。如果keystore文件是在其他位置，那么就需要在Tomcat配置文件中的&lt;Factory&gt;元素上添加一个keystoreFile属性，或者在admintool的Connector (8443)节点指定文件的位置。</p>
<p align=left><strong><font size=2><em>Tomcat</em><em>启动时，得到这样的异常&#8220;</em><em>"java.io.FileNotFoundException: Keystore was tampered with, or password was incorrect</em><em>&#8221;。</em></font></strong></p>
<p align=left>假设没有人改变了您的keystore文件，最有可能的原因是Tomcat使用了与创建keystore文件时使用的不同的密码。要解决这个问题，可以返回并重新创建keystore文件，也可以在Tomcat配置文件中的&lt;Factory&gt;元素上、或者在的Connector (8443)节点上添加keystorePass属性——提醒--密码是大小写敏感的。</p>
<p align=left><strong><font size=2><em>如果仍然有问题，</em></font></strong></p>
<p align=left>如果仍然有问题，邮件列表是一个好的信息资源。可以在<a href="http://jakarta.apache.org/site/mail.html" target=_blank><u><font color=#0000ff>http://jakarta.apache.org/site/mail.html</font></u></a>上查看这个列表中以前的消息，还可以预订和取消预订信息。</p>
<p align=left><strong><font size=3>有关SSL的进一步信息</font></strong></p>
<p align=left>更多信息请参阅&lt;<em>JWSDP_HOME</em>&gt;/docs/tomcat/ssl-howto.html上的Tomcat文档<em>SSL Configuration HOW-TO</em>。</p>
<p align=left><strong><font size=5><font size=4>JAX-RPC的安全性</font></font></strong></p>
<p align=left>在本节，将学习如何配置基于JAX-RPC的Web服务应用程序以进行HTTP/SSL上的基本和相互身份验证。如果验证对于您来说还是一个新题目，那么请参见<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.2.htm#wp80613"><u><font color=#0000ff>验证Web资源的用户</font></u></a>那一节。</p>
<p align=left>在本教程中，我们要修改&lt;<em>JWSDP_HOME</em>&gt;/docs/tutorial/examples/jaxrpc/hello中的示例应用程序以添加HTTP/S基本和相互身份验证。可以在目录&lt;<em>JWSDP_HOME</em>&gt;/docs/tutorial/examples/jaxrpc/security中找到所得到的应用程序。下列步骤是向hello例子中添加基本验证的必要步骤：</p>
<p align=left>&#183;&nbsp; 创建相应的证书和keystore(参见第<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp88453"><u><font color=#0000ff>1步：为基本身份验证创建SSL证书</font></u></a>)。</p>
<p align=left>&#183;&nbsp; 确保为服务器配置了SSL 连接器。Tomcat服务器没有配置SSL Connector，所以需要添加SSL Connector(参见<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp80838"><u><font color=#0000ff>配置SSL连接器</font></u></a>)并在生成的keystore文件中添加信息(见<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp84217"><u><font color=#0000ff>第2步：配置SSL 连接器</font></u></a>)。</p>
<p align=left>&#183;&nbsp; .向部署描述符web.xml添加安全元素。有关操作信息参见<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp83663"><u><font color=#0000ff>第3步：向web.xml添加安全元素</font></u></a>。</p>
<p align=left>&#183;&nbsp; 在应用程序的build.properties文件中修改端点地址，并添加运行这个例子所需要的其他属性。参见<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp84601"><u><font color=#0000ff>第4步：编辑Build属性</font></u></a>。</p>
<p align=left>&#183;&nbsp; 在客户端代码中设置安全属性。有关操作信息参见<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp88161"><u><font color=#0000ff>第5步：在客户端代码中设置安全属性</font></u></a>。</p>
<p align=left>&#183;&nbsp; 编译并运行Web服务。有关对示例应用程序完成这项操作的信息见<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp84817"><u><font color=#0000ff>第6步：创建NewAnt Target以运行这个例子</font></u></a>。</p>
<p align=left>在这里描述为Web服务配置HTTP/S上的基本身份验证的步骤。对于相互身份验证，完成下述步骤，再按<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp83711"><u><font color=#0000ff>启用SSL上的相互验证</font></u></a>中的说明添加客户端验证。</p>
<p align=left><strong><font size=4>第1步：为基本身份验证创建SSL证书</font></strong></p>
<p>
<hr align=left width="100%" SIZE=2>
<p>&#160;</p>
<p align=left>注：在<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp80737"><u><font color=#0000ff>设置数字证书</font></u></a>中对这些信息有更详细的讨论。本节总结了为这个例子创建SSL证书所需要的步骤。</p>
<p>
<hr align=left width="100%" SIZE=2>
<p>&#160;</p>
<p align=left>我们将用工具keytool生成SSL证书并将它们导出为相应的服务器和客户端keystore。记住服务器和客户端keystore是在运行keytool的目录中创建的。因为我们是向hello Web服务添加安全性，所以我们从修改的示例应用程序所在目录中运行keytool，这就是<em>&lt;JWSDP_HOME&gt;</em>/docs/tutorial/examples/jaxrpc/security目录。这样，keystore就在security Web服务的代码所在的同一目录中生成。</p>
<p align=left>1.&nbsp;&nbsp;&nbsp;&nbsp; 运行keytool以生成服务器和客户端keystore。对于基本身份验证，只需要将服务器证书导入到客户端keystore。用默认密码changeit生成服务器keystore。</p>
<p align=left relative="">要生成服务keystore，在终端窗口输入下面内容。在继续之前确是在<em>&lt;JWSDP_HOME&gt;</em>/docs/tutorial/examples/jaxrpc/security目录中。</p>
<p align=left relative="">注意在按Enter时，keytool提示您输入服务器名、组织单位、组织、所在地、州和国家代码。注意<strong>必须在</strong><strong>keytool</strong><strong>的要求输入姓和名的第一个提示中输入服务器名</strong>。为了进行测试，可以用localhost。这个主机必须与在<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp84601"><u><font color=#0000ff>第4步：编辑Build属性</font></u></a>中指定的端点地址中标识的主机相匹配。</p>
<blockquote>
<p align=left relative="relative">&lt;<em>J2SE_HOME</em>&gt;\bin\keytool -genkey<br>-alias tomcat-server<br>-keyalg RSA -keypass changeit<br>-storepass changeit<br>-keystore server.keystore </p>
</blockquote>
<p align=left>2.&nbsp;&nbsp;&nbsp;&nbsp; 导出生成的服务器证书。</p>
<blockquote>
<p align=left relative="relative">&lt;<em>J2SE_HOME</em>&gt;\bin\keytool -export -alias tomcat-server <br>-storepass changeit -file server.cer -keystore<br>server.keystore </p>
</blockquote>
<p align=left>3.&nbsp;&nbsp;&nbsp;&nbsp; 生成客户端keystore。</p>
<p align=left relative="">要生成客户端keystore，在终端窗口中输入以下内容：</p>
<p align=left relative="">注意在按Enter时，keytool提示您输入客户端的服务器名、组织单位、组织、所在地、州和国家代码。注意<strong>必须在</strong><strong>keytool</strong><strong>的要求输入姓和名的第一个提示中输入服务器名</strong>。在大多数情况下，为了进行测试，可以使用localhost。这个主机必须与在<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp84601"><u><font color=#0000ff>第4步：编辑Build属性</font></u></a>中指定的端点地址中标识的主机相匹配。</p>
<blockquote>
<p align=left relative="relative">&lt;<em>J2SE_HOME</em>&gt;\bin\keytool -genkey -alias jwsdp-client -keyalg<br>RSA -keypass changeit -storepass changeit -keystore<br>client.keystore </p>
</blockquote>
<p align=left>4.&nbsp;&nbsp;&nbsp;&nbsp; 将服务器证书导入客户端的keystore。</p>
<blockquote>
<p align=left relative="relative">&lt;<em>J2SE_HOME</em>&gt;\bin\keytool -import -v -trustcacerts <br>-alias tomcat-server -file server.cer <br>-keystore client.keystore -keypass changeit <br>-storepass changeit </p>
</blockquote>
<p align=left><strong><font size=4>第2步：配置SSL连接器</font></strong></p>
<p>
<hr align=left width="100%" SIZE=2>
<p>&#160;</p>
<p align=left>注：在<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp80838"><u><font color=#0000ff>配置SSL 连接器</font></u></a>一节中更详细地提供了配置SSL 连接器的步骤。本节中的步骤是为了您的方便提供的。</p>
<p>
<hr align=left width="100%" SIZE=2>
<p>&#160;</p>
<p align=left>需要为Tomcat配置一个SSL Connector。如果使用不同的服务器，参见<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp80838"><u><font color=#0000ff>配置SSL 连接器</font></u></a>中配置SSL Connector的一般信息。除了为服务配置一个SSL 连接器外，还必须在添加SSL连接器的同一位置上添加有关keystore文件的信息及其密码。例如，在Java WSDP中，首先需要从SSL Connector周围删除注释标签(&lt;!-- ... --&gt;)，然后在<em>&lt;JWSDP_HOME&gt;</em>/conf/server.xml文件中的这一部分添加以<strong>粗体</strong>显示的信息。</p>
<blockquote>
<p align=left>&lt;!-- SSL Connector on Port 8443 --&gt;<br>&lt;Connector className="org.apache.coyote.tomcat4.CoyoteConnector"<br>&nbsp;&nbsp;&nbsp;&nbsp;port="8443" minProcessors="5" maxProcessors="75"<br>&nbsp;&nbsp;&nbsp;&nbsp;enableLookups="false"<br>&nbsp;&nbsp;&nbsp;&nbsp;acceptCount="10" debug="0" scheme="https" secure="true"&gt;<br>&lt;Factory className= "org.apache.coyote.tomcat4.CoyoteServerSocketFactory"&gt;<br><strong>keystoreFile=</strong><br><strong>&nbsp;&nbsp;"&lt;JWSDP_HOME&gt;/docs/tutorial/examples/jaxrpc/security/<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;server.keystore"</strong><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;keystorePass="changeit"</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;clientAuth="false" protocol="TLS" debug="0" /&gt;<br>&lt;/Connector&gt;</strong>&nbsp;</p>
</blockquote>
<p align=left><strong><font size=4>第3步：向web.xml添加安全元素</font></strong></p>
<p align=left>这个例子的文件在<em>&lt;JWSDP_HOME&gt;</em>/docs/tutorial/examples/jaxrpc/security目录中。要通过SSL进行身份验证，web.xml文件包括&lt;security-constraint&gt;、 &lt;login-config&gt;,和&lt;security-role&gt;元素。在基本<em>&lt;JWSDP_HOME&gt;</em>/docs/tutorial/examples/jaxrpc/hello例子之上添加了<strong>粗体</strong>的代码。</p>
<blockquote>
<p align=left>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br>&lt;!DOCTYPE web-app<br>&nbsp;&nbsp;&nbsp; PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application<br>&nbsp;&nbsp; &nbsp;2.3//EN" <br>&nbsp;&nbsp;&nbsp; "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd"&gt;</p>
<p align=left>&nbsp; &lt;web-app&gt;<br>&nbsp; &lt;display-name&gt;Hello World Application (secure)&lt;/display-name&gt;<br>&nbsp; &lt;description&gt;HTTPS example using JAX-RPC &lt;/description&gt;<br>&nbsp; &lt;session-config&gt;<br>&nbsp;&nbsp;&nbsp; &lt;session-timeout&gt;60&lt;/session-timeout&gt;<br>&nbsp; &lt;/session-config&gt;<br>&nbsp;&lt;security-constraint&gt;<br>&nbsp;&nbsp;&nbsp; &lt;web-resource-collection&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;web-resource-name&gt;SecureHello&lt;/web-resource-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;url-pattern&gt;/security&lt;/url-pattern&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;http-method&gt;GET&lt;/http-method&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;http-method&gt;POST&lt;/http-method&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/web-resource-collection&gt;<br>&nbsp;&nbsp;&nbsp; &lt;auth-constraint&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;role-name&gt;manager&lt;/role-name&gt;<br>&nbsp;&nbsp;&nbsp; &lt;/auth-constraint&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;user-data-constraint&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;transport-guarantee&gt;CONFIDENTIAL&lt;/transport-guarantee&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/user-data-constraint&gt;<br>&nbsp; &lt;/security-constraint&gt;<br>&nbsp; &lt;login-config&gt;<br>&nbsp;&nbsp;&nbsp; &lt;auth-method&gt;BASIC&lt;/auth-method&gt;<br>&nbsp; &lt;/login-config&gt;<br>&nbsp;&nbsp;&lt;security-role&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;role-name&gt;manager&lt;/role-name&gt;<br>&nbsp;&nbsp;&lt;/security-role&gt;<br>&lt;/web-app&gt;</p>
</blockquote>
<p align=left>注意&lt;role-name&gt;元素指定了manager，这是一个已经在Tomcat的部署描述符(&lt;<em>JWSDP_HOME&gt;</em>/conf/tomcat-users.xml)中指定的角色。有关角色定义和链接的更多内容，参见<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.1.htm#wp85926"><u><font color=#0000ff>安全性角色</font></u></a>。</p>
<p align=left><strong><font size=4>第4步：编辑Build属性</font></strong></p>
<p align=left>要以HTTP/SSL上的基本身份验证运行这个应用程序，我们向build.properties文件添加与keystore文件相关的一些属性及其密码，这个文件位于<em>&lt;JWSDP_HOME&gt;</em>/docs/tutorial/examples/jaxrpc/security目录中。下述例子假设运行的是Java WSDP 1.1。以<strong>粗体</strong>标记的项被添加到文件中。在后面一节中运行它时，所添加的项将作为参数传递给客户端应用程序。</p>
<blockquote>
<p align=left># This file is referenced by the build.xml file.</p>
<p align=left>&nbsp; example=security<br>context-path=security-jaxrpc</p>
<p align=left>&nbsp; client-class=security.HelloClient<br>client-jar=${example}-client.jar<br>&nbsp; portable-war=${example}-portable.war<br>deployable-war=${context-path}.war war-path=${tut- root}/tutorial/examples/jaxrpc/${example}/dist/${deployable- war}</p>
<p align=left>&nbsp; trust-store=${tut-root}/tutorial/examples/jaxrpc/security/cli-<br>ent.keystore<br>&nbsp; trust-store-password=changeit</p>
</blockquote>
<p align=left><strong><font size=4>第5步：在客户端代码中设置安全属性</font></strong></p>
<p align=left>客户端的源代码在<em>&lt;JWSDP_HOME&gt;</em>/docs/tutorial/examples/jaxrpc/security目录中的<a href="http://java.sun.com/webservices/docs/1.1/tutorial/examples/jaxrpc/security/HelloClient.java"><u><font color=#0000ff>HelloClient.java</font></u></a>文件中。对于SSL上的基本身份验证，客户端代码必须设置几项安全相关的属性。</p>
<p align=left>&#183;&nbsp; &nbsp;trust-store属性——trust-store属性的值是客户端&nbsp;keystore文件的完全限定名：&lt;<em>JWSDP_HOME</em>&gt;/docs/tutorial/examples/jaxrpc/security/client.keystore</p>
<p align=left>&#183;&nbsp; trust-store-password属性——trust-store-password属性是keystore的密码。这个密码的默认值是changeit。</p>
<p align=left>&#183;&nbsp; username和 password属性——username和password属性对应于manager角色(见<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.1.htm#wp85926"><u><font color=#0000ff>安全性角色</font></u></a>)。</p>
<p align=left>客户端如下设置前述的安全属性。<strong>粗体</strong>的代码是在原来的jaxrpc/hello示例应用程序上添加的代码。</p>
<blockquote>
<p align=left>package security; &nbsp; import javax.xml.rpc.Stub;</p>
<p align=left>public class HelloClient {</p>
<p align=left>public static void main(String[] args) {</p>
<p align=left>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>&nbsp;&nbsp;if (args.length !=4) {</strong> <strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;System.out.println("Usage: ant run-security");<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;System.exit(1);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</strong></strong></p>
<p align=left><strong>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String trustStore=args[0];</strong><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String trustStorePassword=args[1];<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String username=args[2];<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String password=args[3];</strong> <strong><br>&nbsp;</strong> <strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("trustStore: " + trustStore);</strong> <strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("trustStorePassword: " +</strong><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;trustStorePassword);</strong><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("username: " + username);</strong><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("password: " + password);</strong></strong></strong></p>
<p align=left>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Stub stub = createProxy();<br><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.setProperty("javax.net.ssl.trustStore",<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;trustStore);</strong><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.setProperty("javax.net.ssl.trustStorePassword",<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;trustStorePassword);</strong><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;stub._setProperty(javax.xml.rpc.Stub.USERNAME_PROPERTY,</strong><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;username);</strong><br><strong>&nbsp;&nbsp;&nbsp;&nbsp;stub._setProperty(javax.xml.rpc.Stub.PASSWORD_PROPERTY,<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;password); </strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HelloIF hello = (HelloIF)stub;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(hello.sayHello("Duke! I feel<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;secure!"));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (Exception ex) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ex.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }</strong></strong></strong></p>
<p align=left>&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; private static Stub createProxy() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Note: MyHelloService_Impl is implementation-specific.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (Stub)(new MyHello_Impl().getHelloIFPort());<br>&nbsp;&nbsp;&nbsp; }<br>}</p>
</blockquote>
<p align=left><strong><font size=4>第6 步：创建New Ant Target以运行这个例子</font></strong></p>
<p align=left>运行hello示例应用程序的现有目标还不足以运行运行安全版本的应用程序。需要传递关于keystore及其密码的信息，以及用户名及用户密码。下列targe添加到<em>&lt;JWSDP_HOME&gt;</em>/docs/examples/jaxrpc/security/build.xml文件中，以便于运行安全的JAX-RPC例子：</p>
<blockquote>
<p align=left>&lt;target name="run-security"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;description="Runs a client with authentication<br>&nbsp;&nbsp;&nbsp;&nbsp;over ssl"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;echo message="Running the ${client-class} program...." /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;java <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fork="on"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classpath="${dist}/${client-jar}:${jwsdp-jars}"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classname="${client-class}" &gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;arg value="${trust-store}" /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;arg value="${trust-store-password}" /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;arg value="${username}" /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;arg value="${password}" /&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/java&gt;<br>&lt;/target&gt;</p>
</blockquote>
<p align=left><strong><font size=4>第7步：编译并运行这个例子</font></strong></p>
<p align=left>要编译并在SSL上运行这个JAX-RPC例子，执行以下步骤：</p>
<p align=left>1.&nbsp;&nbsp;&nbsp;&nbsp; 如果没有做的话，按照<a href="http://java.sun.com/webservices/docs/1.1/tutorial/doc/GettingStarted2.html#wp72683"><u><font color=#0000ff>设置</font></u></a>中的指示，下载本教程的示例代码，完成上述第1到2步，因为这些步骤是对您的计算机和实现特定的。</p>
<p align=left>2.&nbsp;&nbsp;&nbsp;&nbsp; 确保Tomcat正在运行。</p>
<p align=left>3.&nbsp;&nbsp;&nbsp;&nbsp; 进入<em>&lt;JWSDP_HOME&gt;</em>/docs/tutorial/examples/jaxrpc/security目录。</p>
<p align=left>4.&nbsp;&nbsp;&nbsp;&nbsp; 键入下述命令：</p>
<blockquote>
<p align=left relative="relative">&nbsp;&nbsp;ant build<br>&nbsp;&nbsp;ant package<br>&nbsp;&nbsp;ant deploy<br>&nbsp;&nbsp;ant build-static<br>&nbsp;&nbsp;ant run-security </p>
</blockquote>
<p align=left>客户端应该显示下述输出：</p>
<blockquote>
<p align=left relative="relative">% ant run-security<br>Buildfile: build.xml<br><br>run-security:<br>[echo] Running the security.HelloClient program...<br>[java] trustStore: &lt;JWSDP_HOME&gt;/docs/tutorial/examples/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jaxrpc/security/client.keystore<br>[java] trustStorePassword: changeit<br>[java] username: your_name<br>[java] password: your_password<br>[java] Hello - secure Duke! I feel secure!<br><br>BUILD SUCCESSFUL </p>
</blockquote>
<p align=left><strong><font size=4>启用SSL上的相互身份验证</font></strong></p>
<p align=left>TJAX-RPC安全性一节对设置服务器端身份验证进行了讨论。本节讨论设置客户端身份验证。当服务器和客户端身份验证都启用时，这称为相互、或者双向验证。在客户端身份验证中，客户端需要提交由你所选择的可以接受的证书颁发机构颁发的证书。至少有两种途径启用客户端身份验证。</p>
<p align=left>1.&nbsp;&nbsp;&nbsp;&nbsp; .配置SSL套接字工厂启用客户端验证。例如，要为Tomcat配置SSL套接字工厂 ，要设置clientAuth="true"，如下面代码示例中的<strong>粗体</strong>部分。以这种方式启用客户端验证，对于所有通过特定SSL端口进入的请求都会要求客户端验证。与对Web服务配置文件所做的所有改变一样，必须停止并重新启动Web服务以使这种改变生效。</p>
<blockquote>
<p align=left relative="relative">&lt;!-- SSL Connector on Port 8443 --&gt;<br>&lt;Connector className="org.apache.coyote.tomcat4.CoyoteConnector"<br>&nbsp;&nbsp;&nbsp;&nbsp;port="8443" minProcessors="5" maxProcessors="75"<br>&nbsp;&nbsp;&nbsp;&nbsp;enableLookups="false"<br>&nbsp;&nbsp;&nbsp;&nbsp;acceptCount="10" debug="0" scheme="https" secure="true"&gt;<br>&lt;Factory className=<br>"org.apache.coyote.tomcat4.CoyoteServerSocketFactory"&gt;<br>keystoreFile=<br>&nbsp;&nbsp;"&lt;JWSDP_HOME&gt;/docs/tutorial/examples/jaxrpc/security/<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;server.keystore"<br>&nbsp;&nbsp;&nbsp;&nbsp;keystorePass="changeit"<br>&nbsp;&nbsp;&nbsp;&nbsp;clientAuth="true" protocol="TLS" debug="0" /&gt;<br>&lt;/Connector&gt; </p>
</blockquote>
<p align=left>2.&nbsp;&nbsp;&nbsp;&nbsp; 如下面<strong>粗体</strong>部分所示。以这种方法启用客户端身份验证，客户端身份验证是对特定的应用程序启用的。</p>
<blockquote>
<p align=left relative="relative">&lt;login-config&gt;<br>&nbsp;&nbsp;&lt;auth-method&gt;CLIENT-CERT&lt;/auth-method&gt;<br>&lt;/login-config&gt; </p>
</blockquote>
<p align=left>3.&nbsp;&nbsp;&nbsp;&nbsp; 如果同时用上述两种方法启用客户端身份验证，那么客户端身份验证将会执行两次。</p>
<p align=left><strong><font size=3>为JAX-RPC安全示例配置相互验证</font></strong></p>
<p align=left>要以相互验证配置并创建JAX-PRC服务，执行<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp83632"><u><font color=#0000ff>JAX-RPC安全性</font></u></a>中的所有步骤，直到ant build-static命令 (包括这一步)。然后执行以下步骤：</p>
<p align=left>1.&nbsp;&nbsp;&nbsp;&nbsp; 生成客户端证书，导出它，再将客户端证书导入服务器keystore，如<a href="http://gceclub.sun.com.cn/staticcontent/html/webservices/web_services_tutorial/sec.18/18.3.htm#wp80804"><u><font color=#0000ff>为相互验证创建客户端证书</font></u></a>中所讨论的。</p>
<p align=left>2.&nbsp;&nbsp;&nbsp;&nbsp; 编辑web.xml以将部署描述符中的登录配置部分的身份验证方法改为CLIENT-CERT。</p>
<blockquote>
<p align=left relative="relative">&lt;login-config&gt;<br>&nbsp;&nbsp;&lt;auth-method&gt;CLIENT-CERT&lt;/auth-method&gt;<br>&lt;/login-config&gt; </p>
</blockquote>
<p align=left>3.&nbsp;&nbsp;&nbsp;&nbsp; :运行这个应用程序：</p>
<blockquote>
<p align=left relative="relative">ant run-security </p>
</blockquote>
<p align=left>客户端应该显示以下一行：</p>
<blockquote>
<p align=left>Hello Duke! I feel secure!</p>
</blockquote>
<p align=left>致谢：本节包括由Rahul Sharma和Beth Stearns编写的&#8220;Web Services Security Configuration&#8221;白皮书中的内容。</p>
<p align=left><strong><font size=5><font size=4>EIS层安全性</font></font></strong></p>
<p align=left>在EIS层，应用程序组件请求到EIS资源的连接。作为这种连接的一部分，EIS可能请求对资源的sign-on。应用程序组件提供者有两设计这种EIS sign-on的选择：</p>
<p align=left>&#183;&nbsp; 使用容器托管的sign-on方式，应用程序组件让容器负责配置并管理EIS sign-on。容器确定用户名和密码以建立到EIS实例的连接。</p>
<p align=left>&#183;&nbsp; 用组件托管的sign-on方式，应用程序组件代码通过加入执行sign-on过程的代码管理EIS sign-on。</p>
<p align=left><strong><font size=4>容器托管的登录(Sign-On)</font></strong></p>
<p align=left>使用容器托管的登录，应用程序组件不必为了登录资源而向getConnection()方法传递任何安全信息。安全信息是由容器提供的，如下面的例子所示。</p>
<blockquote>
<p align=left>// Business method in an application<br>component Context initctx = new InitialContext();<br>// Perform JNDI lookup to obtain a connection factory javax.resource.cci.ConnectionFactory cxf =<br>&nbsp;&nbsp;&nbsp;&nbsp; (javax.resource.cci.ConnectionFactory)initctx.lookup(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "java:comp/env/eis/MainframeCxFactory");<br>// Invoke factory to obtain a connection. The security<br>// information is not passed in the getConnection method<br>javax.resource.cci.Connection cx = cxf.getConnection();<br>...</p>
</blockquote>
<p align=left><strong><font size=4>组件托管的登录</font></strong></p>
<p align=left>使用组件管理的登录，应用程序组件负责向getConnection()方法传递登录资源所需要的安全信息。安全信息可以是用户名和密码，如下例所示：</p>
<blockquote>
<p align=left>// Method in an application<br>component Context initctx = new InitialContext();<br>// Perform JNDI lookup to obtain a connection factory javax.resource.cci.ConnectionFactory cxf =<br>&nbsp;&nbsp;&nbsp;&nbsp; (javax.resource.cci.ConnectionFactory)initctx.lookup(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "java:comp/env/eis/MainframeCxFactory");<br>// Get a new ConnectionSpec<br>com.myeis.ConnectionSpecImpl properties = //..<br>// Invoke factory to obtain a connection<br>properties.setUserName("...");<br>properties.setPassword("...");<br>javax.resource.cci.Connection cx =<br>&nbsp;&nbsp;cxf.getConnection(properties);<br>...</p>
</blockquote>
<img src ="http://www.blogjava.net/junky/aggbug/118791.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-21 09:00 <a href="http://www.blogjava.net/junky/archive/2007/05/21/118791.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>keytool - 密钥和证书管理工具(1)</title><link>http://www.blogjava.net/junky/archive/2007/05/18/118420.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Fri, 18 May 2007 09:21:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/18/118420.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/118420.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/18/118420.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/118420.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/118420.html</trackback:ping><description><![CDATA[管理由私钥和认证相关公钥的 X.509 证书链组成的密钥仓库（数据库）。还管理来自可信任实体的证书。<br>结构<br>keytool [ 命令 ]<br>说明<br>keytool 是个密钥和证书管理工具。它使用户能够管理自己的公钥/私钥对及相关证书，用于（通过数字签名）自我认证（用户向别的用户/服务认证自己）或数据完整性以及认证服务。它还允许用户储存他们的通信对等者的公钥（以证书形式）。 <br>证书是来自一个实体（个人、公司等）的经数字签名的声明，它声明某些其它实体的公钥（及其它信息）具有某一的特定值（参见证书）。当数据被数字化签名后，校验签名即可检查数据的完整性和真实性。完整性的意思是数据没有被修改或损坏过，真实性的意思是数据的确是来自声称创建了该数据和对它进行了签名的实体。 <br>keytool 将密钥和证书储存在一个所谓的密钥仓库中。缺省的密钥仓库实现将密钥仓库实现为一个文件。它用口令来保护私钥。 <br>jarsigner 工具利用密钥仓库中的信息来产生或校验 Java 存档 (JAR) 文件的数字签名 （JAR 文件将类文件、图象、声音和/或其它数字化数据打包在一个文件中）。jarsigner 用 JAR 文件所附带的证书（包含于 JAR 文件的签名块文件中）来校验 JAR 文件的数字签名，然后检查该证书的公钥是否&#8220;可信任&#8221;，即是否包括在指定的密钥仓库中。 <br>请注意：keytool 和 jarsigner 工具完全取代了 JDK 1.1 中提供的 javakey 工具。这些新工具所提供的功能比 javakey 提供的多，包括能够用口令来保护密钥仓库和私钥，以及除了能够生成签名外还可以校验它们。新的密钥仓库体系结构取代了 javakey 所创建和管理的身份数据库。可以利用 -identitydb keytool 命令将信息从身份数据库导入 密钥仓库。 <br>密钥仓库项<br>在密钥仓库中有两种不同类型的项： <br>密钥项 - 每项存放极为敏感的加密密钥信息，这种信息以一种受保护的格式储存以防止未授权的访问。通常，储存在这类项中的密钥是机密密钥，或是伴有用于认证相应公钥用的证书&#8220;链&#8221;的私钥。keytool 和 jarsigner 工具只处理后一类型的项，即私钥及其关联的证书链。 <br><br>可信任的证书项 - 每项包含一个属于另一团体的公钥证书。它之所以叫做&#8220;可信任的证书&#8221;，是因为密钥仓库的拥有者相信证书中的公钥确实属于证书&#8220;主体&#8221;（拥有者）识别的身份。证书签发人通过对证书签名来保证这点。 <br>密钥仓库使用的别名<br>对所有的密钥仓库项（密钥项和可信任的证书项）的访问都要通过唯一的别名来进行。别名不区分大小写，即别名 Hugo 和 hugo 指的是同一密钥仓库项。 <br>当用 -genkey 命令来生成密钥对（公钥和私钥）或用 -import 命令来将证书或证书链加到可信任证书的清单中，以增加一个实体到密钥仓库中，必须指定了一个别名。后续 keytool 命令必须使用这一相同的别名来引用该实体。 <br>例如，假设您用别名 duke 生成了新的公钥/私钥密钥对并将公钥用以下命令打包到自签名证书中（参见证书链）： <br>keytool -genkey -alias duke -keypass dukekeypasswd<br>这指定了一个初始口令&#8220;dukekeypasswd&#8221;，接下来的命令都要使用该口令才能访问与别名 duke 相关联的私钥。以后如果您想更改 duke 的 私钥口令，可用类似下述的命令： <br>keytool -keypasswd -alias duke -keypass dukekeypasswd -new newpass<br>这将把口令从&#8220;dukekeypasswd&#8221;改为&#8220;newpass&#8221;。 <br>请注意：实际上，除非是作为测试目的或是在安全的系统上，否则不应在命令行或脚本中指定口令。如果没有在命令行上指定所要求的口令选项，您将会得到要求输入口令的提示。当在口令提示符下键入口令时，口令将被即时显示出来（键入什么就显示什么），因此，要小心，不要当着任何人的面键入口令。<br>密钥仓库位置<br>每个 keytool 命令都有一个 -keystore 选项，用于指定 keytool 管理的密钥仓库的永久密钥仓库文件名称及其位置。缺省情况下，密钥仓库储存在用户宿主目录（由系统属性的&#8220;user.home&#8221;决定）中名为 .keystore 的文件中。在 Solaris 系统中&#8220;user.home&#8221;缺省为用户的宿主目录。<br>密钥仓库的创建<br>当用 -genkey、-import 或 -identitydb 命令向某个尚不存在的密钥仓库添加数据时，就创建了一个密钥仓库。 <br>具体地说，如果在 -keystore 选项中指定了一个并不存在的密钥仓库，则该密钥仓库将被创建。 <br><br>如果不指定 -keystore 选项，则缺省密钥仓库将是宿主目录中名为 .keystore 的文件。如果该文件并不存在，则它将被创建。<br>密钥仓库实现<br>java.security 包中提供的 KeyStore 类为访问和修改密钥仓库中的信息提供了相当固定的接口。可以有多个不同的具体实现，其中每个实现都是对某个特定类型的密钥仓库的具体实现。 <br>目前，有两个命令行工具（keytool 和 jarsigner）以及一个名为 Policy Tool 的基于 GUI 的工具使用密钥仓库实现。由于密钥仓库是公用的，JDK 用户可利用它来编写其它的安全性应用程序。 <br>Sun Microsystems 公司提供了一个内置的缺省实现。它利用名为&#8220;JKS&#8221; 的专用密钥仓库类型（格式），将密钥仓库实现为一个文件。它用个人口令保护每个私钥，也用口令（可能为另一个口令）保护整个密钥仓库的完整性。 <br>密钥仓库的实现基于提供者 (provider)。更具体地说，由密钥仓库所提供的应用程序接口是借助于&#8220;服务提供者接口&#8221;(SPI) 来实现的。也就是说，在 java.security 包中还有一个对应的抽象 KeystoreSpi 类，它定义了&#8220;提供者&#8221;必须实现的服务提供者接口方法。（术语&#8220;提供者&#8221;指的是一个或一组包，这个或这组包提供了一部份可由 Java 安全 API 访问的服务子集的具体实现。因此，要提供某个密钥仓库实现，客户机必须实现一个&#8220;提供者&#8221;并实现 KeystoreSpi 子类，如如何为 Java 加密体系结构实现 Provider 中所述。 <br>通过使用 KeyStore 类中提供的&#8220;getInstance&#8221;工厂方法，应用程序可从不同的提供者中挑选不同类型的密钥仓库实现。密钥仓库类型定义密钥仓库信息的存储和数据格式，以及用于保护密钥仓库中的私钥和密钥仓库自身完整性的算法。不同类型的密钥仓库实现是不兼容的。 <br>keytool 使用基于文件的密钥仓库实现 （它把在命令行中传递给它的密钥仓库位置当成文件名处理并将之转换为文件输入流，从该文件输入流中加载密钥仓库信息）。另一方面，jarsigner 和 policytool 工具可从任何可用 URL 指定的位置读取某个密钥仓库。<br><br>对于 keytool 和 jarsigner，可在命令行用 -storetype 选项指定密钥仓库类型。对于 Policy Tool，可通过 &#8220;编辑&#8221;菜单中的&#8220;更改密钥仓库&#8221;命令来指定密钥仓库类型。 <br>如果没有明确指定一个密钥仓库类型，这些工具将只是根据安全属性文件中指定的 keystore.type 属性值来选择密钥仓库实现。安全属性文件名为 java.security，它位于 JDK 安全属性目录 java.home/lib/security 中，其中 java.home 为 JDK 的安装目录。 <br>每个工具都先获取 keystore.type 的值，然后检查所有当前已安装的提供者直到找到一个实现所要求类型的密钥仓库的实现为止。然后就使用该提供者的密钥仓库实现。 <br>KeyStore 类定义了一个名为 getDefaultType 的静态方法，它可让应用程序或 applet 检索 keystore.type 属性的值。以下代码将创建缺省密钥仓库类型（此类型由 keystore.type 属性所指定。）的一个实例： <br>KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());<br>缺省的密钥仓库类型是&#8220;jks&#8221;（这是由&#8220;SUN&#8221;提供者提供的密钥仓库实现的专用类型）。它在安全性属性文件中由下行进行指定： <br>keystore.type=jks<br>要让工具使用不同于缺省类型的密钥仓库实现，可更改此行，指定不同的密钥仓库类型。 <br>例如，如果您有一个这样的提供者包，它给出一个名为&#8220;pkcs12&#8221;的密钥仓库类型的密钥仓库实现，则可将上面那行改为： <br>keystore.type=pkcs12<br>注意：密钥仓库类型的命名中大小写无关紧要。例如，&#8220;JKS&#8221;将被认为是与&#8220;jks&#8221;相同的。<br>支持的算法和密钥大小<br>keytool 允许用户指定任何注册了的加密服务提供者所提供的密钥对生成和签名算法。也就是说，各种命令中的 keyalg 和 sigalg 选项必须得到提供者的实现的支持。缺省的密钥对生成算法是&#8220;DSA&#8221;。签名算法是从所涉及私钥的算法推导来的：如果所涉及的私钥是&#8220;DSA&#8221;类型，则缺省的签名算法为&#8220;SHA1withDSA&#8221;，如果所涉及的私钥是&#8220;RSA&#8221;类型，则缺省的签名算法为&#8220;MD5withRSA&#8221;。 <br>在生成 DSA 密钥对时，密钥大小的范围必须在 512 到 1024 位之间，且必须是 64 的倍数。缺省的密钥大小为 1024 位。<br><br>
<img src ="http://www.blogjava.net/junky/aggbug/118420.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-18 17:21 <a href="http://www.blogjava.net/junky/archive/2007/05/18/118420.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>keytool - 密钥和证书管理工具(3)</title><link>http://www.blogjava.net/junky/archive/2007/05/18/118419.html</link><dc:creator>junky</dc:creator><author>junky</author><pubDate>Fri, 18 May 2007 09:20:00 GMT</pubDate><guid>http://www.blogjava.net/junky/archive/2007/05/18/118419.html</guid><wfw:comment>http://www.blogjava.net/junky/comments/118419.html</wfw:comment><comments>http://www.blogjava.net/junky/archive/2007/05/18/118419.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/junky/comments/commentRss/118419.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/junky/services/trackbacks/118419.html</trackback:ping><description><![CDATA[当导入新的可信任证书时，密钥仓库中还没有 alias。在把证书添加到密钥仓库中之前，keytool 将尝试用密钥仓库中已有的可信任证书来构造从该证书到自签名证书（属于根 CA）的信任链，以对证书进行校验。 <br>如果指定了 -trustcacerts 选项，则将为该信任链考虑其它证书，即考虑名为&#8220;cacerts&#8221;的文件中的证书，该文件位于 JDK 安全属性目录 java.home\lib\security 中，其中 java.home 为 JDK 安装目录。&#8220;cacerts&#8221;文件代表含有 CA 证书的系统范围的密钥仓库。通过指定密钥仓库类型为&#8220;jks&#8221;，系统管理员可用 keytool 来配置和管理该文件。&#8220;cacerts&#8221;密钥仓库文件发送时附有五个 VeriSign 根 CA 证书，其 X.500 特征名如下： <br>1. OU=Class 1 Public Primary Certification Authority, O="VeriSign, Inc.",<br>C=US<br>2. OU=Class 2 Public Primary Certification Authority, O="VeriSign,<br>Inc.", C=US<br>3. OU=Class 3 Public Primary Certification Authority,<br>O="VeriSign, Inc.", C=US<br>4. OU=Class 4 Public Primary Certification<br>Authority, O="VeriSign, Inc.", C=US<br>5. OU=Secure Server Certification<br>Authority, O="RSA Data Security, Inc.", C=US<br>&#8220;cacerts&#8221;密钥仓库文件的初始口令为&#8220;changeit&#8221; 。系统管理员在安装 JDK 后，就应该立即更改这个口令以及该文件的缺省访问权限。 <br>如果 keytool 无法建立从要导入的证书到自签名证书的信任路径（利用密钥仓库或&#8220;cacerts&#8221;文件），则打印出该证书的信息，而用户将得到要求校验的提示，例如，系统将通知用户通过比较显示出的指纹和得自其它（可信任的）信息来源的指纹来进行校验，信息来源可能是证书拥有者本人。在将证书作为一个&#8220;可信任&#8221;证书导入之前，要十分小心，务必保证该证书是有效的！ -- 参见有关导入可信任证书的警告。然后，用户可以选择中止导入操作。但是，如果给了 -noprompt 选项，则不会有与用户的交互。<br><br>当导入认证答复时，该认证答复将用密钥仓库中可信任的证书来确认，有时也使用在&#8220;cacerts&#8221;密钥仓库文件中配置的证书（如果指定了 -trustcacerts 选项）。 <br>如果答复是一个 X.509 证书，keytool 将尝试建立信任链，以该认证答复为头，以属于根 CA 的自签名证书为尾。该认证答复和用于认证该认证答复的证书层次形成了 alias 的新证书链。 <br>如果答复是 PKCS#7 格式的证书链，则该链应首先被排序（用户证书在最前面，自签名的根 CA 证书在最后面），然后 keytool 尝试将答复中的根 CA 证书与密钥仓库或&#8220;cacerts&#8221;密钥仓库文件（如果指定了 -trustcacerts 选项）中的任何可信任证书进行匹配。如果找不到匹配，则打印出该根 CA 证书的信息，而用户将得到要求校验它的提示，例如，系统将通知用户通过比较显示出的指纹和得自其它（可信任的）信息来源的指纹来进行校验，信息来源可能是证书拥有者本人。因此，用户可以选择中止导入操作。但是，如果给了 -noprompt 选项，则不会有与用户的交互。 <br>alias 的新证书链将取代与该项关联的旧证书链。只有提供了有效的 keypass，即提供了用于保护该项的私钥的口令时，旧链才可被取代。如果没有提供口令，而且私钥口令与密钥仓库口令不同，用户将得到要求输入口令的提示。使用口令时必须小心 -- 参见与口令有关的警告。 <br>-selfcert {-alias alias} {-sigalg sigalg} {-dname dname} {-validity valDays} [-keypass keypass] {-storetype storetype} {-keystore keystore} [-storepass storepass] {-v} {-Jjavaoption} <br>利用密钥仓库信息（包括与 alias 关联的私钥和公钥）产生 X.509 v1 自签名证书。如果在命令行提供了 dname，它将同时用作该证书的签发人和主体的 X.500 特征名。否则，将使用与 alias 关联的 X.500 特征名（位于其现有证书链底部）。 <br><br>所生成的证书作为 alias 所标识的密钥仓库项中的单元素证书链来存储，它将取代现有的证书链。 <br>sigalg 指定签名证书用的算法。参见支持的算法和密钥大小。 <br>要访问私钥，必须提供正确的口令，因为私钥在密钥仓库中是受口令保护的。如果在命令行中没有提供 keypass，且私钥口令与保护密钥仓库完整性所用的口令不同，则用户将得到要求输入该口令的提示。使用口令时必须小心 -- 参见与口令有关的警告。 <br>valDays 指定证书的有效期。 <br>-identitydb {-file idb_file} {-storetype storetype} {-keystore keystore} [-storepass storepass] {-v} {-Jjavaoption} <br>从 idb_file 文件中读取 JDK 1.1.x 格式的身份数据库，并将它的项加到密钥仓库中。如果没有给出文件名，则从标准输入设备中读取身份数据库。如果不存在密钥仓库，则创建它。 <br>只有被标记为可信任的身份数据库项（&#8220;身份&#8221;）才能被导入密钥仓库中。所有其它身份都将被略去。对每个可信任的身份，将创建一个密钥仓库项。身份名用作该密钥仓库项的&#8220;别名&#8221;。 <br>所有可信任身份的私钥都将在相同的口令 storepass 下得到加密。该口令与保护密钥仓库完整性所用的口令相同。用户随后可用 keytool 命令选项&#8220;-keypasswd&#8221;来对各私钥赋予单独的口令。 <br>身份数据库中的一个身份可以存放多个证书，各证书所认证的都是同一公钥。但一个私钥的密钥仓库密钥项含有该私钥和单一的&#8220;证书链&#8221;（该链最初只有一个证书），链中的第一个证书含有与该私钥对应的公钥。当从身份导入信息时，只有该身份中的第一个证书被储存到密钥仓库中。这是因为身份数据库中的身份名被用作其相应密钥仓库项的别名，而别名在密钥仓库中是唯一的。<br>导出数据<br>-certreq {-alias alias} {-sigalg sigalg} {-file certreq_file} [-keypass keypass] {-storetype storetype} {-keystore keystore} [-storepass storepass] {-v} {-Jjavaoption} <br><br>生成 PKCS#10 格式的证书签名请求 (CSR)。 <br>CSR 用来发送给认证机构 (CA)。CA 对认证请求者进行认证（通常是离线的），并返回证书或证书链，以取代密钥仓库中现有的证书链（该证书链最初只含有自签名证书）。 <br>私钥和与 alias 关联的 X.500 特征名用于创建 PKCS#10 证书请求。要访问私钥，必须提供正确的口令，因为私钥在库中是受口令保护的。如果在命令行没有提供 keypass，且私钥口令与保护密钥仓库完整性所用的口令不同，则用户将得到要求输入口令的提示。 <br>使用口令时必须小心 -- 参见与口令有关的警告。 <br>sigalg 指定签名 CSR 时用的算法。参见支持的算法和密钥大小。 <br>CSR 存储在文件 certreq_file 中。如果没有给出文件名，CSR 将被输出到标准输出设备中。 <br>用 import 命令来导入 CA 所返回的答复。 <br>-export {-alias alias} {-file cert_file} {-storetype storetype} {-keystore keystore} [-storepass storepass] {-rfc} {-v} {-Jjavaoption} <br>从密钥仓库中读取与 alias 关联的证书，并将其储存在文件 cert_file 中。 <br>如果没有给出文件名，证书将被输出到标准输出设备中。 <br>缺省情况下，证书被输出为二进制编码格式，但如果指定了 -rfc 选项，则将被输出为 Internet RFC 1421 标准中定义的可打印格式。 <br>如果 alias 引用的是可信任证书，则该证书将被输出。否则，alias 引用的是含有相关证书链的密钥项。在这种情况下，链中的第一个证书将被返回。该证书对由 alias 所指定的实体的公钥进行认证。<br>显示数据<br>-list {-alias alias} {-storetype storetype} {-keystore keystore} [-storepass storepass] {-v | -rfc} {-Jjavaoption} <br>打印（到标准输出设备中）alias 所标识的密钥仓库项的内容。如果没有指定别名，则将打印整个密钥仓库的内容。 <br>缺省情况下，该命令打印证书的 MD5 指纹。如果指定了 -v 选项，证书将以可读格式打印，同时包含拥有者、签发人和序列号等附加信息。如果指定了??-rfc 选项，证书将以 Internet RFC 1421 标准所定义的可打印的编码格式打印。 <br><br>不能同时指定 -v 和 -rfc 两个选项。 <br>-printcert {-file cert_file} {-v} {-Jjavaoption} <br>从文件 cert_file 中读取证书将以可读格式打印其内容。如果没有给出文件名，则从标准输入设备中读取证书。 <br>证书可以是用二进制编码或 Internet RFC 1421 标准所定义的可打印编码格式。 <br>注意：该选项的使用与密钥仓库无关。<br>管理密钥仓库<br>-keyclone {-alias alias} [-dest dest_alias] [-keypass keypass] [-new new_keypass] {-storetype storetype} {-keystore keystore} [-storepass storepass] {-v} {-Jjavaoption} <br>生成新的密钥仓库项，该项含有的私钥和证书链与原始项的相同。 <br>原始项由 alias （如果没有提供别名，则其值缺省为&#8220;mykey&#8221;）标识。新（目标）项由 dest_alias 标识。如果没有在命令行中提供目标别名，用户将得到要求输入该信息的提示。 <br>如果私钥口令与密钥仓库口令不同，那么，只有提供了有效的 keypass 时该项才能被复制。keypass 是用于保护与 alias 关联的私钥的口令。如果没有在命令行提供密钥口令，且私钥口令与密钥仓库口令不同，用户将得到要求输入口令的提示。如果愿意，可用不同的口令来保护复制项中的私钥。如果没有在命令行提供 -new 选项，用户将得到提示要求输入新项的口令（可以选择让该口令与被复制项的私钥所用的口令相同）。 <br>使用口令时必须小心 -- 参见与口令有关的警告。 <br>该命令可用于建立多个与给定密钥对相对应的证书链，或用于备份。 <br>-storepasswd [-new new_storepass] {-storetype storetype} {-keystore keystore} [-storepass storepass] {-v} {-Jjavaoption} <br><br>更改保护密钥仓库内容的完整性所用的口令。新口令为 new_storepass，其长度必须至少是 6 个字符。 <br>使用口令时必须小心 -- 参见与口令有关的警告。 <br>-keypasswd {-alias alias} [-keypass old_keypass] [-new new_keypass] {-storetype storetype} {-keystore keystore} [-storepass storepass] {-v} {-Jjavaoption} <br>把保护 alias 所标识的私钥的口令从 old_keypass 更改为 new_keypass。 <br>如果没有在命令行提供 keypass 选项，且私钥口令与密钥仓库口令不同，则用户将得到要求输入该口令的提示。 <br>如果没有在命令行给出 -new 选项，则用户将得到要求输入新口令的提示。 <br>使用口令时必须小心 -- 参见与口令有关的警告。 <br>-delete [-alias alias] {-storetype storetype} {-keystore keystore} [-storepass storepass] {-v} {-Jjavaoption} <br>从密钥仓库中删除 alias 所标识的项。如果没有在命令行上提供别名，则用户将得到要求输入别名的提示。 <br>获取帮助<br>-help <br>列出所有的命令及其选项。 <br>示例<br>假设您要创建一个密钥仓库以管理您的公钥/私钥对来自您所信任实体的证书。 <br>生成密钥对<br>您首先要做的是创建一个密钥仓库和生成密钥对。您可以使用以下命令： <br>keytool -genkey -dname "cn=Mark Jones, ou=JavaSoft, o=Sun, c=US"<br>-alias business -keypass kpi135 -keystore /working/mykeystore<br>-storepass ab987c -validity 180<br>（请注意：键入该命令时必须使其成为一行。此处用多行来显示，主要是为了可读性。） <br>该命令将在 C 盘的&#8220;working&#8221;目录（假设它还不存在）中创建名为&#8220;mykeystore&#8221;的密钥仓库，并赋予它口令&#8220;ab987c&#8221;。它将为实体生成公钥/私钥对，该实体的&#8220;特征名&#8221;为：常用名&#8220;Mark Jones&#8221;、组织单位&#8220;JavaSoft&#8221;、组织&#8220;Sun&#8221;和两个字母的国家代码&#8220;US&#8221;。它使用缺省的&#8220;DSA&#8221;密钥生成算法来创建密钥，两个密钥（公钥与私钥）的长度都是 1024 位。 <br><br>它创建自签名证书（使用缺省的&#8220;带 DSA 的 SHA1&#8221;签名算法），该证书包括公钥和特征名信息。该证书的有效期为 180 天，且与别名&#8220;business&#8221;所代表的密钥仓库项关联。私钥被赋予口令&#8220;kpi135&#8221;。 <br>如果采用选项的缺省值，可以大大缩短该命令。实际上，这些选项并不是必需的；对于有缺省值的选项，未指定时将使用缺省值，对于任何被要求的值，您将会得到要求输入它的提示。因此，您可以只使用下面的命令： <br>keytool -genkey<br>这种情况下，将创建一个具有别名&#8220;mykey&#8221;的密钥仓库项，它含有新生成的密钥对和有效期为 90 天的证书。该项被放在您的宿主目录下一个名为&#8220;.keystore&#8221;的密钥仓库中（如果该密钥仓库并不存在，则将创建它）。您将得到要求输入特征名信息、密钥仓库口令和私钥口令的提示。 <br>其余示例假设您执行了未指定选项的 -genkey 命令，且用上述第一个 -genkey 命令中给出的值来回答提示要求（私钥口令为&#8220;kpi135&#8221;等等）。<br>从认证机构请求已签名的证书<br>目前为止我们所具有的就是自签名证书。证书如果由认证机构 (CA) 签名，将更有可能得到别人的信任。要得到这样的签名，首先要用以下命令生成证书签名请求 (CSR)： <br>keytool -certreq -file MarkJ.csr<br>这将为缺省别名&#8220;mykey&#8221;所标识的实体生成 CSR，并将此请求放在名为&#8220;MarkJ.csr&#8221;的文件中。将此文件提交给某个 CA（例如 VeriSign, Inc.）。该 CA 将对您这个请求者进行认证（通常是离线的），然后返回它们所签名的证书，用于认证您的公钥。（某些情况下，它们实际上将返回证书链，链中每个证书都认证前一个证书的签名人的公钥。）<br>导入 CA 的证书<br>自签名证书必须用证书链代替，链中的每个证书都认证前一个证书的签名人的公钥，直到&#8220;根&#8221;CA 为止。 <br>在导入 CA 的答复之前，在您的密钥仓库中或 cacerts 密钥仓库文件（如导入命令中所述）中需要有一个或多个&#8220;可信任&#8221;证书： <br><br>如果该认证答复是个证书链，您只需要链中最顶部的证书（即用于认证根 CA 的公钥的&#8220;根&#8221; CA 证书）。 <br>如果该认证答复是单个证书，您需要发放 CA（即签名该证书的认证机构）的证书，如果此证书不是自签名的，则需其签名人的证书，以此类推，直到自签名的&#8220;根&#8221; CA 证书为止。 <br>&#8220;cacerts&#8221;密钥仓库文件发送时附有五个 VeriSign 根 CA 证书，因此您可能并不需要导入 VeriSign 证书以作为密钥仓库中的可信任证书。但如果您请求由另一个 CA 签名的证书，而认证该 CA 的公钥的证书未被加到&#8220;cacerts&#8221;中，则您需要将来自该 CA 的证书作为&#8220;可信任证书&#8221;导入。 <br>来自 CA 的证书通常是自签名的或是由另一个 CA 签名的（这种情况下您还需要认证该 CA 的公钥的证书）。假设 ABC, Inc. 公司是 CA，而您从该公司获得一个声称是自签名证书的名为&#8220;ABCCA.cer&#8221;的文件，它用于认证该 CA 的公钥。 <br>在将证书作为一个&#8220;可信任&#8221;证书导入之前，要十分小心，务必保证该证书是有效的！先查看一下（用 keytool -printcert 命令，或用不带 -noprompt 选项的 keytool -import 命令）以确保所显示的证书指纹与所预计的相匹配。然后可以给发送证书的人打电话，将您所看到的指纹与他们所提供的（或安全公钥储存库所显示的）进行比较。只有两者相等才可保证证书在传送途中没有被其它人（例如，攻击者）的证书所更换。如果发生了这样的攻击，而您未检查证书即将其导入，那么您就会信任攻击者所签名的任何东西。 <br>如果您相信证书是有效的，则您可以用以下命令将其加到密钥仓库中： <br>keytool -import -alias abc -file ABCCA.cer<br>这将在密钥仓库中用文件&#8220;ABCCA.cer&#8221;中的数据创建&#8220;可信任证书&#8221;项，并将别名&#8220;abc&#8221;赋予该项。<br>导入来自 CA 的认证答复<br>一旦导入了用于认证 CA（该 CA 是您将证书签名请求送往之处）公钥的证书后，或在&#8220;cacerts&#8221;文件中已有这种证书时，就可以导入该认证答复，从而用证书链取代您的自签名证书。如果 CA 的答复是证书链，则该链是 CA 响应您的请求而返回的证书链；如果 CA 的答复是一个证书，则该链是用认证答复和可信任证书建立的证书链，这些可信任证书是密钥仓库（您要将认证答复导入之处）或&#8220;cacerts&#8221;密钥仓库文件中已有的。 <br>例如，假设您将证书签名请求送往 VeriSign。您可用以下命令来导入认证答复，该命令假定所返回的证书名为&#8220;VSMarkJ.cer&#8221;： <br>keytool -import -trustcacerts -file VSMarkJ.cer<br>导出用于认证您的公钥的证书<br>假设您用 jarsigner 工具来签名 Java 归档 (JAR) 文件。需要使用这一文件的客户机将认证您的签名。 <br>认证签名的一种方法是先将您的公钥证书作为&#8220;可信任&#8221;项导入它们的密钥仓库中。您可以将证书导出并将其提供给客户机。例如，假设项的别名为&#8220;mykey&#8221;，您可以用以下命令将您的证书导出到名为 MJ.cer 的文件中： <br>keytool -export -alias mykey -file MJ.cer<br>有了该证书以及已被签名的 JAR 文件，客户机就可以用 jarsigner 工具来认证您的签名。<br>更改特征名但保留密钥对<br>假设，譬如说因为您换了部门或搬到另一个城市去了而改变了您的特征名。如果愿意，您仍然可以使用您先前使用的公钥/私钥对而只对特征名进行更新。例如，假设您的名字叫 Susan Miller，并用别名 sMiller 和以下的特征名创建了初始密钥项： <br>"cn=Susan Miller, ou=Finance Department, o=BlueSoft, c=us"<br>假设您从财务部门换到了会计部门。您仍然可使用先前所生成的公钥/私钥对，而用以下方法对特征名进行更新。首先，复制您的密钥项： <br>keytool -keyclone -alias sMiller -dest sMillerNew<br>（您将得到要求输入密钥仓库口令和初始密钥口令及目标密钥口令的提示，因为在命令行没有提供这些信息。）现在，您需要更改与复制项关联的证书链以使链中的第一个证书使用您的新特征名。先用相应名称生成自签名证书： <br>keytool -selfcert -alias sMillerNew<br>-dname "cn=Susan Miller, ou=Accounting Department, o=BlueSoft, c=us"<br>然后根据该新证书中的信息生成证书签名请求： <br>keytool -certreq -alias sMillerNew<br>当您得到 CA 认证答复后，将其导入： <br>keytool -import -alias sMillerNew -file VSSMillerNew.cer<br>导入认证答复后，您也许会要删除使用旧特征名的初始密钥项： keytool -delete -alias sMiller<br><br>
<img src ="http://www.blogjava.net/junky/aggbug/118419.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/junky/" target="_blank">junky</a> 2007-05-18 17:20 <a href="http://www.blogjava.net/junky/archive/2007/05/18/118419.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>