﻿<?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-All in Blending-随笔分类-安全</title><link>http://www.blogjava.net/javaniu/category/13640.html</link><description>Blending</description><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 06:49:18 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 06:49:18 GMT</pubDate><ttl>60</ttl><item><title>JAAS示例</title><link>http://www.blogjava.net/javaniu/archive/2006/07/31/61103.html</link><dc:creator>Gary Niu</dc:creator><author>Gary Niu</author><pubDate>Mon, 31 Jul 2006 15:58:00 GMT</pubDate><guid>http://www.blogjava.net/javaniu/archive/2006/07/31/61103.html</guid><wfw:comment>http://www.blogjava.net/javaniu/comments/61103.html</wfw:comment><comments>http://www.blogjava.net/javaniu/archive/2006/07/31/61103.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/javaniu/comments/commentRss/61103.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/javaniu/services/trackbacks/61103.html</trackback:ping><description><![CDATA[
		<strong>
				<font face="Verdana" size="4">概述</font>
		</strong>
		<br />
		<br />
		<p>最后，我们开始讨论本教程最有趣的部分：边学边做。我们从运行比较全面的 JAASExample 应用程序开始，它包含我们已经在教程中讨论过的所有认证和授权机制（和代码）。运行该示例并查看了第一次配置的结果之后，我们将研究一些不同的配置选项并马上检查结果。在本教程的这最后一章结束时，您将初次体验 JAAS 编程的乐趣。</p>
		<strong>
				<font face="Verdana" size="4">运行示例</font>
		</strong>
		<br />
		<br />
		<font face="Verdana" size="2"> </font>
		<p>设计 JAASExample 应用程序的目的是为了演示几种认证和授权技术以及一些配置设置的影响。用下列语句开始运行该示例，可以在教程源文件（请参阅参考资料<binref href="JavaSecurity2-source.jar"></binref>）中的文件 run.bat 中找到这些语句。</p>
		<pre>
				<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace"> java -Djava.security.manager
-Djava.security.auth.login.config==login.config
-Djava.security.policy==jaas.policy JAASExample 
</code>
		</pre>
		<p>语句指示系统的缺省安全性管理器使用名为 login.config 的登录配置文件，使用名为 jaas.policy 的安全性策略文件，最后运行主应用程序 JAASExample。注：双等于号（<code><font face="Courier" size="1">==</font></code>）表明系统缺省登录配置和策略文件<i>不</i>应该添加到已在这里列出的各项中。一个等于号（<code><font face="Courier" size="1">=</font></code>）表明应该将文件与系统缺省值并置。<br /><br /><strong><font face="Verdana" size="4">示例结果和说明</font></strong><br /><br /><font face="Verdana" size="2"> </font></p>
		<p>下面是运行 JAASExample 的结果：</p>
		<pre>
				<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
AlwaysLoginModule Login
Username? Brad

Login: AlwaysLoginModule SUCCESS

PasswordLoginModule Login
Username? joeuser
Password? joepw

Login: PasswordLoginModule Username Matches
Login: PasswordLoginModule Password Matches
Login: PasswordLoginModule SUCCESS
Commit: AlwaysLoginModule SUCCESS
Commit: PasswordLoginModule SUCCESS

OVERALL AUTHENTICATION SUCCEEDED

<code><font face="Courier" size="1">Subject</font></code>:
           <code><font face="Courier" size="1">Principal</font></code>: Brad
           <code><font face="Courier" size="1">Principal</font></code>: joeuser

joeuser has Payroll access

<code><font face="Courier" size="1">Subject</font></code> has Personnel access

Logout: AlwaysLoginModule SUCCESS
Logout: PassswordLoginModule SUCCESS
</code>
		</pre>
		<p>下面详尽地描述了上面结果中的正常执行情况：</p>
		<ol>
				<li>
						<code>
								<font face="Courier" size="1">login.config</font>
						</code> 定义两个登录模块；<code><font face="Courier" size="1">AlwaysLoginModule</font></code> 是必需的。它先运行。<br /><br /></li>
				<li>
						<code>
								<font face="Courier" size="1">AlwaysLoginModule</font>
						</code> 在登录阶段启动，它调用回调处理程序来获取用户名（<code><font face="Courier" size="1">Brad</font></code>）。登录成功。<br /><br /></li>
				<li>第二个登录模块 <code><font face="Courier" size="1">PasswordLoginModule</font></code> 是可选的。它接下来运行，调用回调处理程序以获取用户名（<code><font face="Courier" size="1">joeuser</font></code>）和密码（<code><font face="Courier" size="1">joepw</font></code>），两者都是匹配的。该登录也是成功的。<br /><br /></li>
				<li>因为必需的和可选的模块都成功，所以在两个登录模块上同时调用 <code><font face="Courier" size="1">commit</font></code>，并且整个认证成功。结果，<code><font face="Courier" size="1">Subject</font></code> 同时包含两个 <code><font face="Courier" size="1">Principal</font></code>：<code><font face="Courier" size="1">Brad</font></code> 和 <code><font face="Courier" size="1">joeuser</font></code>。<br /><br /></li>
				<li>使用程序性授权的工资单程序检查 <code><font face="Courier" size="1">joeuser</font></code> 是否在 <code><font face="Courier" size="1">Subject</font></code> 的 <code><font face="Courier" size="1">Principal</font></code> 集中，如果是，授予它访问权。<br /><br /></li>
				<li>使用声明性授权的职员信息程序查看 jaas.policy 文件中是否有授权语句，授予 <code><font face="Courier" size="1">Brad</font></code><code><font face="Courier" size="1">PersonnelPermission</font></code> 的特权，所以它也成功。<br /><br /></li>
				<li>两个登录模块同时注销。 </li>
		</ol>
		<p>
				<strong>
						<font face="Verdana" size="4">失败的认证</font>
				</strong>
				<br />
				<br />
				<font face="Verdana" size="2"> </font>
		</p>
		<p>只是为了好玩，让我们看一下当我们出差错时会发生什么情况。在下面的示例中，设置是相同的，但我们将为 <code><font face="Courier" size="1">joeuser</font></code> 输入一个错误密码。请亲自检查下面的输出，看一下它与上面的结果有何不同。</p>
		<pre>
				<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
AlwaysLoginModule Login
Username? Brad

Login: AlwaysLoginModule SUCCESS

PasswordLoginModule Login
Username? joeuser
Password? wrongpw

Login: PasswordLoginModule Username Matches
Login: PasswordLoginModule Password Mismatch
Login: PasswordLoginModule FAIL
Commit: AlwaysLoginModule SUCCESS
Commit: PasswordLoginModule FAIL

OVERALL AUTHENTICATION SUCCEEDED

<code><font face="Courier" size="1">Subject</font></code>:
           <code><font face="Courier" size="1">Principal</font></code>: Brad

Payroll Access DENIED
<code><font face="Courier" size="1">Subject</font></code> has Personnel access

Logout: AlwaysLoginModule SUCCESS
Logout: PasswordLoginModule SUCCESS
</code>
		</pre>
		<p>正如您所看到的那样，<code><font face="Courier" size="1">PasswordLoginModule</font></code> 登录已经失败。但是，因为这个模块在 login.config 文件中配置为 <code><font face="Courier" size="1">optional</font></code>，所以总体认证仍是成功的。区别在于只有 <code><font face="Courier" size="1">Brad Principal</font></code> 被添加到 <code><font face="Courier" size="1">Subject</font></code> 中。工资单程序找不到 <code><font face="Courier" size="1">joeuser Principal</font></code>，所以访问被拒绝。职员信息程序可以将 <code><font face="Courier" size="1">Brad Principal</font></code> 与 <code><font face="Courier" size="1">Brad</font></code> 授权语句匹配，所以它被成功添加并授予访问权。</p>
		<p>在接下来的几页中，我们将对 login.config 文件的配置作一些修改，然后检查每种新配置的结果。</p>
		<p>
		</p>
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr>
								<td width="90%">
										<font face="Verdana, Arial, Helvetica" size="4">
												<b>变体 1：登录配置</b>
										</font>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<font face="Verdana" size="2"> </font>
		<p>首先，让我们看一下当我们将 login.config 文件更改为：为了认证成功，<i>两个</i>登录模块都是必需的，会是什么情况。新的 config 文件是：</p>
		<pre>
				<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
JAASExample {
      AlwaysLoginModule required;
      PasswordLoginModule required;
};
</code>
		</pre>
		<p>这里是结果输出：</p>
		<pre>
				<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
AlwaysLoginModule Login
Username? Brad

Login: AlwaysLoginModule SUCCESS

PasswordLoginModule Login
Username? joeuser
Password? wrongpw

Login: PasswordLoginModule Username Matches
Login: PasswordLoginModule Password Mismatch
Login: PasswordLoginModule FAIL
Abort: AlwaysLoginModule SUCCESS
Logout: AlwaysLoginModule SUCCESS
Abort: PasswordLoginModule FAIL

OVERALL AUTHENTICATION FAILED
</code>
		</pre>
		<p>当 <code><font face="Courier" size="1">joeuser</font></code> 输入错误密码时，<code><font face="Courier" size="1">PasswordLoginModule</font></code> 就会象先前那样失败。然而，因为该模块是必需的，所以运行异常终止阶段并且总体认证失败。不执行任何敏感代码。<br /><br /><strong><font face="Verdana" size="4">变体 2：PAM 的能力</font></strong></p>
		<font face="Verdana" size="2">
		</font>
		<p>该变体是为演示可插入的认证模块的实用程序而设计的。我们回到原始的 login.config 文件，即 <code><font face="Courier" size="1">AlwaysLoginModule</font></code> 是必需的，而 <code><font face="Courier" size="1">PasswordLoginModule</font></code> 是可选的，然后将 <code><font face="Courier" size="1">NTLoginModule</font></code> （或适用于您的平台的任何其它模块）添加到该文件。新模块将是 <code><font face="Courier" size="1">required</font></code>。修改后的 login.config 文件应该看起来如下：</p>
		<pre>
				<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
JAASExample {
      AlwaysLoginModule required;
      PasswordLoginModule optional;
      com.sun.security.auth.module.NTLoginModule required;
};
</code>
		</pre>
		<p>接下来，运行示例。在下面的输出中，您将看到已经添加了一个新的认证方法以及几个全新的 <code><font face="Courier" size="1">Principal</font></code>（和一个公用凭证）。</p>
		<pre>
				<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
AlwaysLoginModule Login
Username? Brad

Login: AlwaysLoginModule SUCCESS

PasswordLoginModule Login
Username? joeuser
Password? joepw

Login: PasswordLoginModule Username Matches
Login: PasswordLoginModule Password Matches
Login: PasswordLoginModule SUCCESS
Commit: AlwaysLoginModule SUCCESS
Commit: PasswordLoginModule SUCCESS

OVERALL AUTHENTICATION SUCCEEDED

<code><font face="Courier" size="1">Subject</font></code>:
           <code><font face="Courier" size="1">Principal</font></code>: Brad
           <code><font face="Courier" size="1">Principal</font></code>: joeuser
           <code><font face="Courier" size="1">Principal</font></code>: NTUserPrincipal: Brad
           <code><font face="Courier" size="1">Principal</font></code>: NTDomainPrincipal: WORKGROUP
           <code><font face="Courier" size="1">Principal</font></code>: NTSidUserPrincipal:
S-1-5-21-2025429265-1580813891-854245398-1004
           <code><font face="Courier" size="1">Principal</font></code>: NTSidPrimaryGroupPrincipal: 
S-1-5-21-2025429265-1580418891-85 4245398-513
           <code><font face="Courier" size="1">Principal</font></code>: NTSidGroupPrincipal: 
S-1-5-21-2025429265-1580818891-854245398-513
           <code><font face="Courier" size="1">Principal</font></code>: NTSidGroupPrincipal: S-1-1-0
           <code><font face="Courier" size="1">Principal</font></code>: NTSidGroupPrincipal: S-1-5-32-544
           <code><font face="Courier" size="1">Principal</font></code>: NTSidGroupPrincipal: S-1-5-32-545
           <code><font face="Courier" size="1">Principal</font></code>: NTSidGroupPrincipal: S-1-5-5-0-49575

           <code><font face="Courier" size="1">Principal</font></code>: NTSidGroupPrincipal: S-1-2-0
           <code><font face="Courier" size="1">Principal</font></code>: NTSidGroupPrincipal: S-1-5-4
           <code><font face="Courier" size="1">Principal</font></code>: NTSidGroupPrincipal: S-1-5-11
           Public Credential: NTNumericCredential: 1240

joeuser has Payroll access

<code><font face="Courier" size="1">Subject</font></code> has Personnel access

Logout: AlwaysLoginModule SUCCESS
Logout: PasswordLoginModule SUCCESS
</code>
		</pre>
		<p>更酷的是，我们甚至不必自己改动我们的应用程序代码。上面所有的更改都是由本机 OS 认证机制完成的。这应该向您暗示了 PAM 的能力。</p>
		<strong>
				<font face="Verdana" size="4">变体 3：策略文件配置</font>
		</strong>
		<br />
		<br />
		<font face="Verdana" size="2"> </font>
		<p>在最后一个变体中，我们将查看当修改访问控制策略时会发生什么情况。我们从修改原始 <code><font face="Courier" size="1">login.config</font></code> 中的 grant 文件开始，以便 <code><font face="Courier" size="1">joeuser</font></code>（而不是 <code><font face="Courier" size="1">Brad</font></code>）有 <code><font face="Courier" size="1">PersonnelPermission</font></code>，如下所示：</p>
		<pre>
				<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
grant <code><font face="Courier" size="1">Principal</font></code> PrincipalImpl "joeuser" {
     permission PersonnelPermission "access";
};
</code>
		</pre>
		<p>接下来，运行应用程序，为 <code><font face="Courier" size="1">joeuser</font></code> 输入错误密码。结果如下所示：</p>
		<pre>
				<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
AlwaysLoginModule Login
Username? Brad

Login: AlwaysLoginModule SUCCESS

PasswordLoginModule Login
Username? joeuser
Password? wrongpw


Login: PasswordLoginModule Username Matches
Login: PasswordLoginModule Password Mismatch
Login: PasswordLoginModule FAIL
Commit: AlwaysLoginModule SUCCESS
Commit: PasswordLoginModule FAIL

OVERALL AUTHENTICATION SUCCEEDED

<code><font face="Courier" size="1">Subject</font></code>:
           <code><font face="Courier" size="1">Principal</font></code>: Brad

Payroll Access DENIED
Personnel Access DENIED
Logout: AlwaysLoginModule SUCCESS
Logout: PasswordLoginModule SUCCESS
</code>
		</pre>
		<p>正如您所看到，<code><font face="Courier" size="1">Subject</font></code> 的 <code><font face="Courier" size="1">Principal</font></code> 集中只有 <code><font face="Courier" size="1">Brad</font></code>。工资单访问和职员信息访问的尝试都已失败。为什么？第一次尝试失败是因为没有名为 <code><font face="Courier" size="1">joeuser</font></code> 的 <code><font face="Courier" size="1">Principal</font></code>，第二次尝试失败是因为对于 <code><font face="Courier" size="1">Brad</font></code> 没有授予权限语句。</p>
		<strong>
				<font face="Verdana" size="4">不要在此止步</font>
		</strong>
		<br />
		<font face="Verdana" size="2"> </font>
		<p>在本章中，我们已经将所有 JAAS 认证和授权片段合在一起来说明完整的 JAAS 应用程序的运行情况。我们还对应用程序做了几次改动以观察它实际上发生了什么以及该体系结构在应用程序安全性方面有多灵活。 </p>
		<p>为了扩展您在这里学到的知识，您应该继续使用 JAAS 并查看当尝试不同的登录配置时会发生什么情况。例如，如果在您的安装中运行着 Kerberos，尝试运行 Kerberos 登录模块。</p>
		<strong>
				<font face="Verdana" size="4">参考资料</font>
		</strong>
		<br />
		<font face="Verdana" size="2"> </font>
		<p>
				<b>下载</b>
		</p>
		<ul>
				<li>下载本教程中使用的完整源代码和类。<binref href="JavaSecurity2-source.jar"></binref><br /><br /></li>
				<li>可从 Sun Microsystems 获得 <a href="http://java.sun.com/j2se/">Java 2 平台，标准版</a>（http://java.sun.com/j2se/）。 </li>
		</ul>
		<p>
				<b>文章、教程和其它在线参考资料</b>
		</p>
		<ul>
				<li>请阅读由 Brad Rubin 编写的本教程系列的第 1 部分，“<a href="http://www-105.ibm.com/developerworks/education.nsf/java-onlinecourse-bytitle/AD6F348A3D5DF4C686256BF90044DE44?OpenDocument">Crypto basics</a>，”（http://www-106.ibm.com/developerworks/education/r-jsec1.html）。<br /><br /></li>
				<li>请参阅 Java 开发人员连接（Java Developer Connection），以获取完整的 <a href="http://java.sun.com/j2se/1.4/docs/guide/security/permissions.html">Java 权限</a>（http://java.sun.com/j2se/1.4/docs/guide/security/permissions.html）的列表。<br /><br /></li>
				<li>虽然本教程中未论及新近与 JDK 1.4 一起提供的 Java 通用安全性服务（Java General Security Service(JGSS)），但它提供了用于在应用程序之间安全地交换消息的通用框架。Sun 最新发布的<a href="http://java.sun.com/j2se/1.4/docs/guide/security/jgss/single-signon.html">白皮书</a>（http://java.sun.com/j2se/1.4/docs/guide/security/jgss/single-signon.html）讨论了如何使用 JAAS、JGSS 和 Kerberos 来提供单点登录应用程序安全性。<br /><br /></li>
				<li>Sun 还拥有几个描述 JAAS 和 JGSS 的不同使用方法和过程的<a href="http://java.sun.com/j2se/1.4/docs/guide/security/jgss/tutorials/">教程和用户指南</a>（http://java.sun.com/j2se/1.4/docs/guide/security/jgss/tutorials/）。有一个极好的参考指南描述了 <a href="http://java.sun.com/j2se/1.4/docs/guide/security/jgss/tutorials/JGSSvsJSSE.html">when to use JGSS versus JSEE</a>（http://java.sun.com/j2se/1.4/docs/guide/security/jgss/tutorials/JGSSvsJSSE.html）。<br /><br /></li>
				<li>请参阅 Sun Microsystems 的 <a href="http://java.sun.com/security">Java Security 站点</a>（http://java.sun.com/security），学习更多有关最新 Java 安全性技术的知识。<br /><br /></li>
				<li>Joseph Sinclair 在系列文章“<a href="http://www-106.ibm.com/developerworks/library/j-secure/index.html">Securing systems</a>”中，提供了一篇“A three-pronged solution for identifying users”（<i>developerWorks</i>，2001 年 6 月，http://www-106.ibm.com/developerworks/library/j-secure/index.html）。 
</li>
				<li>一旦您掌握了基本知识，Carlos Fonseca 将向您展示如何“<a href="http://www-106.ibm.com/developerworks/java/library/j-jaas/index.html">Extend JAAS for class instance-level authorization</a>”（<i>developerWorks</i>，2002 年 4 月，http://www-106.ibm.com/developerworks/library/j-jaas/）。<br /><br /></li>
				<li>在“<a href="http://www-106.ibm.com/developerworks/java/library/j-gssapi/index.html">Enhance Java GSSAPI with a login interface using JAAS</a>”中，Thomas Owusu 提供了有关凭证和秘钥的一些颇有见地的观点（<i>developerWorks</i>，2001 年 11 月，http://www-106.ibm.com/developerworks/library/j-gssapi/）。<br /><br /></li>
				<li>查找 WebSphere Portal Server 1.2 如何<a href="http://www7b.software.ibm.com/wsdd/library/techarticles/0110_gilmore/gilmore.html">实现 JAAS 和单点登录安全性</a>（WebSphere 开发者园地，2001 年 10 月）。 </li>
		</ul>
		<p>
				<b>书籍</b>
		</p>
		<ul>
				<li>有关 Web 安全性和 Java 技术的全面讨论，请参阅由 O'Reilly 于 2002 年出版的 <i><a href="http://www.oreilly.com/catalog/websec2/">Web Security, Privacy, and Commerce，第二版</a></i>（由 Simson Garfinkel 和 Gene Spafford 合著）。<br /><br /></li>
				<li>如果您想更进一步了解 Java 安全性，请参阅 Wrox Press 于 2001 年出版的 <i><a href="http://www.amazon.com/exec/obidos/ASIN/1861004257/104-8739833-1347930">Professional Java Security</a></i>（http://www.amazon.com/exec/obidos/ASIN/1861004257/104-8739833-1347930）（由 Jess Garms 和 Daniel Somerfield 合著）。<br /><br /></li>
				<li>另一本学习 Java 安全性的优秀参考资料是由 O'Reilly &amp; Associates 于 2001 年出版的 <i><a href="http://www.amazon.com/exec/obidos/ASIN/0596001576">Java Security</a></i>（http://www.amazon.com/exec/obidos/ASIN/0596001576）（Scott Oaks 著）。<br /><br /></li>
				<li>从 <i><a href="http://www.counterpane.com/sandl.html">Secrets and Lies: Digital Security in a Networked World</a></i>（http://www.counterpane.com/sandl.html，Bruce Schneier 著，2000 年）中可以找到人们为了生存和竞争需要知道哪些安全性方面的知识。<br /></li>
				<li>如果您想着重了解认证技术，请参阅 <i><a href="http://www.aw.com/catalog/academic/product/1,4096,0201615991,00.html">Authentication: From passwords to public keys</a></i> （http://www.aw.com/catalog/academic/product/1,4096,0201615991,00.html，Richard E. Smith 著，由 Addison-Wesley 于 2002 年出版）。 </li>
		</ul>
		<p>
				<b>其它参考资料</b>
		</p>
		<ul>
				<li>
						<a href="file:///C:/developerWorks/cgi-bin/click.cgi?url=http://www.research.ibm.com/javasec/&amp;origin=j">IBM Java Security Research</a> 页面（http://www.research.ibm.com/javasec/）详细描述了开发中的各种安全性项目。<br /><br /></li>
				<li>请访问 <a href="file:///C:/developerWorks/cgi-bin/click.cgi?url=http://www-106.ibm.com/developerworks/tivoli/&amp;origin=j">Tivoli 开发者园区</a>（http://www-106.ibm.com/developerworks/tivoli/），以帮助您构建和维护电子商务的安全性。<br /><br /></li>
				<li>
						<i>developerWorks</i> 的<a href="file:///C:/developerWorks/cn/security/index.shtml">安全性特殊主题</a>为开发人员提供了包含一般安全性主题的实用技术信息。<br /><br /></li>
				<li>参与由 Paul Abbott 主管的 <i>developerWorks</i><a href="http://www-105.ibm.com/developerworks/java_df.nsf/AllViewTemplate?OpenForm&amp;RestrictToCategory=44"> Java 安全性论坛</a>。<br /><br /></li>
				<li>您将在 <i>developerWorks</i><a href="http://www-106.ibm.com/developerworks/java/">Java 技术专区</a>（http://www-106.ibm.com/developerworks/java/）中找到上百篇有关 Java 编程各个方面的文章。<br /><br /></li>
				<li>请参阅 <a href="http://www-105.ibm.com/developerworks/education.nsf/dw/java-onlinecourse-bytitle">developerWorks 教程页面</a>（http://www-105.ibm.com/developerworks/education.nsf/dw/java-onlinecourse-bytitle），以便从 <i>developerWorks</i> 获取与 Java 技术相关的免费教程的完整列表。 </li>
		</ul>
		<br />
<img src ="http://www.blogjava.net/javaniu/aggbug/61103.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/javaniu/" target="_blank">Gary Niu</a> 2006-07-31 23:58 <a href="http://www.blogjava.net/javaniu/archive/2006/07/31/61103.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>受权</title><link>http://www.blogjava.net/javaniu/archive/2006/07/31/61101.html</link><dc:creator>Gary Niu</dc:creator><author>Gary Niu</author><pubDate>Mon, 31 Jul 2006 15:52:00 GMT</pubDate><guid>http://www.blogjava.net/javaniu/archive/2006/07/31/61101.html</guid><wfw:comment>http://www.blogjava.net/javaniu/comments/61101.html</wfw:comment><comments>http://www.blogjava.net/javaniu/archive/2006/07/31/61101.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/javaniu/comments/commentRss/61101.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/javaniu/services/trackbacks/61101.html</trackback:ping><description><![CDATA[
		<table cellspacing="0" cellpadding="0" border="0">
				<tbody>
						<tr>
								<td width="90%">
										<font face="Verdana, Arial, Helvetica" size="4">
												<b>概述</b>
										</font>
								</td>
						</tr>
				</tbody>
		</table>
		<font face="Verdana" size="2">
		</font>
		<p>
				<font face="Verdana">了解 Java 平台如何实现授权的访问控制对于了解我们将在本章中讨论的概念很重要。Java 平台使用<i>访问控制环境（access control context）</i>的概念来确定当前执行线程的权限。从概念上讲，可以将它视作与每个执行线程连接的令牌。在 JAAS 之前，访问控制基于了解当前 Java .class 文件的代码来源或数字签名者的身份。在这种模型下，访问控制是基于了解代码出自于何处。有了 JAAS，我们将模型转了个向。通过将 <code><font size="1">Subject</font></code> 添加到访问控制环境，我们可以开始根据谁正在执行（或要求执行）一段给定代码来授予或拒绝访问权。</font>
		</p>
		<p>
				<font face="Verdana">在本章中，您将了解 JAAS 的用于控制对敏感代码访问的机制。我们将首先描述授权在 JAAS 中是如何工作的，然后继续更深入地描述授权框架的每个组件。在本章的最后，我们将给出在较大型的运行示例中使用的一些代码样本，它们演示了程序性授权和声明性授权技术。读完本章之后，您应该清楚地知道 JAAS 的认证和授权机制如何一起工作来保护基于 Java 的系统。</font>
				<br />
				<br />
				<font face="Verdana" size="4">
						<strong>访问控制和权限<br /></strong>
						<font size="2"> </font>
				</font>
		</p>
		<p>因为执行线程可以跨越多个具有不同环境特征的模块，所以 Java 平台实现了<i>最小特权</i>这一概念。在属于给定执行线程的整个调用程序栈中，调用栈的成员具有不同特征，用于确定权限的结果是所有这些特征的交集或最小公分母。例如，如果一段调用代码有受限权限（可能由于未对它签名，所以它不可信），但它调用一段信任度较高的代码（可能有一个签名），则<i>降低</i>被调用代码中的权限来匹配较低的信任度。</p>
		<p>将包含在访问控制环境中的权限特征与策略文件中的 Java 权限 <code><font face="Courier" size="1">grant</font></code> 语句进行比较，以表明是否允许敏感操作。这是由名为 <code><font face="Courier" size="1">AccessController</font></code> 的 Java 实用程序完成的，它的接口用于通过程序检查特权以及将当前的 <code><font face="Courier" size="1">Subject</font></code> 与活动的访问控制环境相关联。（较旧的 Java 安全性管理器（Java Security Manager）接口已经过时，所以一定要使用 <code><font face="Courier" size="1">AccessController</font></code> 方法。）<br /></p>
		<p>
				<br />
				<strong>将 Subject 绑定到访问控制环境<br /></strong>
				<font size="2"> </font>
		</p>
		<p>因为可以在应用程序启动之后认证 <code><font face="Courier" size="1">Subject</font></code>，所以必须有一个将 <code><font face="Courier" size="1">Subject</font></code> 动态绑定到访问控制环境的方法，以创建一个包含代码权限（从何处装入它以及谁对它进行签名）和用户权限（<code><font face="Courier" size="1">Subject</font></code>）环境。为此，我们使用方法 <code><font face="Courier" size="1">Object doAs(Subject subject, PrivilegedAction action)</font></code>。这个 <code><font face="Courier" size="1">doAs</font></code> 方法调用特别为授权设计的类，该类实现 <code><font face="Courier" size="1">PrivilegedAction</font></code> 接口。</p>
		<p>如果不使用线程的当前方法，则可以使用另一种调用方法 <code><font face="Courier" size="1">Object doAsPrivileged(Subject, PrivilegedAction action, AccessControlContext acc)</font></code> 来指定访问控制环境。它的特殊用法是将 <code><font face="Courier" size="1">AccessControlContext</font></code> 设置为空，在 <code><font face="Courier" size="1">doAsPrivileged</font></code> 调用发生时，可以使调用栈短路，并且在 <code><font face="Courier" size="1">PrivilegedAction</font></code> 对象中时，允许<i>增加</i>权限。稍后，当对象返回到调用程序时，将减少权限。本教程稍后将说明这两种技术。</p>
		<p>
				<code>
						<font face="Courier" size="1">doAs</font>
				</code>和 <code><font face="Courier" size="1">doAsPrivileged</font></code> 方法形式上都允许抛出 <code><font face="Courier" size="1">PrivilegedActionException</font></code>。</p>
		<p>
				<strong>权限</strong>
				<br />
				<br />
				<font size="2"> </font>
		</p>
		<p>Java 平台有许多用于控制对系统资源的访问的内置权限。例如：</p>
		<pre>
				<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
grant signedBy "Brad", codeBase "http://www.bradrubin.com" {
       permission java.io.FilePermission "/tmp/abc", "read";
};

</code>
		</pre>
		<p>允许由“Brad”签名的并从“http://www.bradrubin.com”装入的代码读取 <code><font face="Courier" size="1">/tmp/abc</font></code> 目录。有关 Java 权限的完整列表，请参阅<a href="file:///C:/Documents%20and%20Settings/gniu/Desktop/j-sec2/j-sec2/j-sec2/j-sec2-6-2.html">参考资料</a>。</p>
		<p>
				<strong>创建您自己的权限</strong>
				<br />
				<font size="2"> </font>
		</p>
		<p>Java 平台允许您创建自己的权限对象。与正规的权限相似，可以将这些对象放在策略文件中，并在部署时配置它们。为了进行演示，请查看下面的 <code><font face="Courier" size="1">PersonnelPermission</font></code>。稍后，我们将使用这些代码以允许访问一些敏感的职员信息操作的代码。</p>
		<pre>
				<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
import java.security.*;
//
// Implement a user defined permission for access to the personnel //
code for this example public class PersonnelPermission extends
BasicPermission {

     public PersonnelPermission(String name) {
       super(name);
     }

     public PersonnelPermission(String name, String action) {
       super(name);
     }
}
</code>
		</pre>
		<p>对于上面的权限，您应该注意以下几点：第一，构造器使用用户定义的特权名称（在这个示例中，只有一种名为 <i>access</i> 的类型）。另外一个构造器使用名为 <i>action</i> 的附加的改进的参数，虽然这里不使用它。对于这个示例，将使用 <code><font face="Courier" size="1">BasicPermission</font></code> 类。如果我们需要更多特性，可以使用 <code><font face="Courier" size="1">Permission</font></code> 类。</p>
		<p>
				<strong>策略文件</strong>
				<br />
				<br />
				<font size="2"> </font>
		</p>
		<p>策略文件是控制对系统资源（包括敏感代码）访问的主要机制。本示例中的策略文件名为 jaas.policy，并且在 Java 命令行中由特性 <code><font face="Courier" size="1">-Djava.security.policy==jaas.policy</font></code> 指定。双等于号（<code><font face="Courier" size="1">==</font></code>）表明将替换系统策略文件，而不是添加到系统策略文件权限中。下面是我们正在本教程中使用的 jaas.policy 文件：</p>
		<pre>
				<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
grant {
     permission javax.security.auth.AuthPermission "createLoginContext";
     permission javax.security.auth.AuthPermission "doAs";
     permission javax.security.auth.AuthPermission "doAsPrivileged";
     permission javax.security.auth.AuthPermission "modifyPrincipals";
     permission javax.security.auth.AuthPermission "getSubject"; };

grant      principal PrincipalImpl "Brad" {
     permission PersonnelPermission "access";
};
</code>
		</pre>
		<p>为了使 JAAS 机制自举，系统必须有某些特权 — 即示例中的前五个。通过这些适当的权限，将访问权 <code><font face="Courier" size="1">PersonnelPermission</font></code>（用户定义的权限）授予“Brad”主体。</p>
		<p>
				<strong>JAAS 主程序示例</strong>
				<br />
				<br />
				<font size="2"> </font>
		</p>
		<p>下面是这个示例的主应用程序（从命令行调用）。它实例化登录环境、然后登录、尝试执行两个敏感对象（一个对象使用程序性授权，另一个对象使用声明性授权），最后注销。接下来，我们将更深入地研究主程序的两个元素：程序性授权和声明性授权。</p>
		<pre>
				<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
import java.security.*;
import javax.security.auth.*;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
//
// This is the main program in the JAAS Example.  It creates a Login  
// Context, logs the user in based on the settings in the Login  
// Configuration file,and calls two sensitive pieces of code, the  
// first using programmatic authorization, and the second using 
// declarative authorization.
public class JAASExample {

     static LoginContext lc = null;

     public static void main( String[] args) {
       //
       // Create a login context
       try {
         lc = new LoginContext("JAASExample",
            new UsernamePasswordCallbackHandler());
       } catch (LoginException le) {
         System.out.println( "Login Context Creation Error" );
         System.exit(1);
       }
       //

       // Login
       try {
         lc.login();
       } catch (LoginException le) {
         System.out.println( "\nOVERALL AUTHENTICATION FAILED\n" );
         System.exit(1);
       }
       System.out.println( "\nOVERALL AUTHENTICATION SUCCEEDED\n" );
       System.out.println( lc.getSubject() );
       //
       // Call the sensitive PayrollAction code, which uses programmatic
       // authorization.
       try {
         <code><font face="Courier" size="1">Subject</font></code>.doAs( lc.getSubject(), new PayrollAction() );
       } catch (AccessControlException e) {
         System.out.println( "Payroll Access DENIED" );
       }
       //
       // Call the sensitive PersonnelAction code, which uses declarative
       // authorization.
       try {
         <code><font face="Courier" size="1">Subject</font></code>.doAsPrivileged( lc.getSubject(), new PersonnelAction(), null );
       } catch (AccessControlException e) {
         System.out.println( "Personnel Access DENIED" );
       }
       try {
         lc.logout();
       } catch (LoginException le) {
         System.out.println( "Logout FAILED" );
         System.exit(1);
       }
       System.exit(0);

     }
}
</code>
		</pre>
		<p>
				<strong>程序性授权示例</strong>
				<br />
				<font size="2"> </font>
		</p>
		<p>在这个示例中，我们将了解如何编码程序权限决定。<code><font face="Courier" size="1">PrivilegedAction</font></code> 类由主 JAASExample 程序的 <code><font face="Courier" size="1">doAs</font></code> 方法调用，因此当它输入 <code><font face="Courier" size="1">run</font></code> 方法时，经认证的 <code><font face="Courier" size="1">Subject</font></code> 被绑定到线程上的应用程序环境。</p>
		<p>我们从访问控制器检索当前的 <code><font face="Courier" size="1">Subject</font></code>，遍历任何包含的经认证的 <code><font face="Courier" size="1">Principal</font></code>，以查找“joeuser”。如果找到他，则可以进行敏感的操作并返回。如果找不到，我们抛出一个 <code><font face="Courier" size="1">AccessControlException</font></code>。显然，在现实生活中我们应该使用更易于管理且可伸缩的技术，而不是将用户名直接硬编码到应用程序中。</p>
		<pre>
				<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
import java.io.*;
import java.security.*;
import javax.security.auth.*;
import javax.security.auth.login.*;
import java.util.*;
//
// This class is a sensitive Payroll function that demonstrates the
// use of programmatic authorization which only allows a subject 
// that contains the principal "joeuser" in class PayrollAction
implements PrivilegedAction {
     public Object run() {
       // Get the passed in subject from the DoAs
       AccessControlContext context = AccessController.getContext();
       <code><font face="Courier" size="1">Subject</font></code> subject = <code><font face="Courier" size="1">Subject</font></code>.getSubject( 
context );
       if (subject == null ) {
         throw new AccessControlException("Denied");
       }
       //
       // Iterate through the principal set looking for joeuser.  If
       // he is not found,
       Set principals = subject.getPrincipals();
       Iterator iterator = principals.iterator();
       while (iterator.hasNext()) {
         PrincipalImpl principal = (PrincipalImpl)iterator.next();
         if (principal.getName().equals( "joeuser" )) {
           System.out.println("joeuser has Payroll access\n");
           return new Integer(0);
         }
       }
       throw new AccessControlException("Denied");
     }
}
<br /><br /><br /><br /><br /><br /><br /><br /><br /><font face="Verdana" size="4"><strong>声明性授权示例<br /></strong><font size="2"></font><p>在这个示例中，我们将通过使用用户定义的权限 <code><font face="Courier" size="1">PersonnelPermission</font></code>，来演示如何用策略文件中的权限授予以声明性授权的方式来控制授权检查。我们只询问 <code><font face="Courier" size="1">AccessController</font></code> 是否已经授予这个权限，如果没有授予，它抛出一个 <code><font face="Courier" size="1">AccessControlException</font></code>，否则，如果已经授予，则保持运行。我们用主 JAASExample 代码中的 <code><font face="Courier" size="1">doAsPrivileged</font></code> 调用和空访问控制环境来调用这个 <code><font face="Courier" size="1">PrivilegedAction</font></code>，以使调用时调用栈短路。因为在将 <code><font face="Courier" size="1">Subject</font></code> 与 <code><font face="Courier" size="1">doAsPrivileged</font></code> 调用中的环境结合之前，<code><font face="Courier" size="1">Subject</font></code> 不是环境的一部分，也未经 grant 语句授权，而且还因为使用了“最小特权”和权限的交集，所以这是必需的，否则将不允许提高权限的级别。</p><pre><code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
import java.io.*;
import java.security.*;
//
// This class is a sensitive Personnel function that demonstrates 
// the use of declarative authorization using the user defined 
// permission PersonnelPermission, which throws an exception 
// if it not granted 
class PersonnelAction implements PrivilegedAction {
     public Object run() {
       AccessController.checkPermission(new PersonnelPermission("access"));
       System.out.println( "<code><font face="Courier" size="1">Subject</font></code> has Personnel access\n");
       return new Integer(0);
     }
}
</code></pre><br /><br /><br /><br /></font></code>
		</pre>
		<p>
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br />
				<br /> </p>
<img src ="http://www.blogjava.net/javaniu/aggbug/61101.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/javaniu/" target="_blank">Gary Niu</a> 2006-07-31 23:52 <a href="http://www.blogjava.net/javaniu/archive/2006/07/31/61101.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>认证</title><link>http://www.blogjava.net/javaniu/archive/2006/07/31/61098.html</link><dc:creator>Gary Niu</dc:creator><author>Gary Niu</author><pubDate>Mon, 31 Jul 2006 15:35:00 GMT</pubDate><guid>http://www.blogjava.net/javaniu/archive/2006/07/31/61098.html</guid><wfw:comment>http://www.blogjava.net/javaniu/comments/61098.html</wfw:comment><comments>http://www.blogjava.net/javaniu/archive/2006/07/31/61098.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/javaniu/comments/commentRss/61098.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/javaniu/services/trackbacks/61098.html</trackback:ping><description><![CDATA[
		<font face="Verdana" size="4">
				<strong>概述<br /><br /></strong>
				<p>本章中，我们将集中讨论 JAAS 中的认证元素。我们将从描述简单的登录和认证过程开始，它将为您提供 JAAS 认证体系结构的高级别视图。接着，我们将详细讨论体系结构的每一部分。本章结束时，您将有机会仔细地研究两个登录模块的代码。</p>
				<p>如果您还没有下载本教程的源代码<binref href="JavaSecurity2-source.jar"></binref>，请您现在就开始下载。该源代码能更好地说明后面讨论中概述的步骤。</p>
				<br />
				<strong>Subject 和 Principal</strong>
		</font>
		<br />
		<br />
		<font face="Verdana, Arial, Helvetica" size="2">
				<p>
						<code>
								<font face="Courier" size="1">Subject</font>
						</code>是一种 Java 对象，它表示单个实体，如个人。一个 <code><font face="Courier" size="1">Subject</font></code> 可以有许多个相关身份，每个身份都由一个 <code><font face="Courier" size="1">Principal</font></code> 对象表示。那么，比方说一个 <code><font face="Courier" size="1">Subject</font></code> 表示要求访问电子邮件系统和财务系统的雇员。该 <code><font face="Courier" size="1">Subject</font></code> 将有两个 <code><font face="Courier" size="1">Principal</font></code>，一个与用于电子邮件访问的雇员的用户标识关联，另一个与用于财务系统访问的用户标识关联。</p>
				<p>
						<code>
								<font face="Courier" size="1">Principal</font>
						</code>不是持久性的，所以每次用户登录时都必须将它们添加到 <code><font face="Courier" size="1">Subject</font></code>。<code><font face="Courier" size="1">Principal</font></code> 作为成功认证过程的一部分被添加到 <code><font face="Courier" size="1">Subject</font></code>。同样，如果认证失败，则从 <code><font face="Courier" size="1">Subject</font></code> 中除去 <code><font face="Courier" size="1">Principal</font></code>。不管认证成功与否，当应用程序执行注销时，将除去所有 <code><font face="Courier" size="1">Principal</font></code>。</p>
				<p>除了包含一组 <code><font face="Courier" size="1">Principal</font></code> 外，<code><font face="Courier" size="1">Subject</font></code> 还可以包含两组凭证：公用和专用。<i>credential</i> 是密码、密钥和令牌等。对公用和专用凭证集的访问是由 Java 特权控制的，稍后，我们将在本教程中讨论它。对凭证的完整讨论超出了本教程的范围。<br /><br /></p>
				<table cellspacing="0" cellpadding="0" border="0">
						<tbody>
								<tr>
										<td width="90%">
												<font face="Verdana, Arial, Helvetica" size="4">
														<b>Subject 的方法</b>
												</font>
										</td>
										<td align="right" width="200">
												<font face="Verdana, Arial, Helvetica" size="1">
												</font>
										</td>
								</tr>
						</tbody>
				</table>
				<font face="Verdana, Arial, Helvetica" size="2">
						<p>
								<code>
										<font face="Courier" size="1">Subject</font>
								</code>对象有几个方法，其中一些方法如下：</p>
						<ul>
								<li>
										<code>
												<font face="Courier" size="1">subject.getPrincipals()</font>
										</code>返回一组 <code><font face="Courier" size="1">Principal</font></code> 对象。因为结果是 <code><font face="Courier" size="1">Set</font></code>，所以适用操作 <code><font face="Courier" size="1">remove()</font></code>、<code><font face="Courier" size="1">add()</font></code> 和 <code><font face="Courier" size="1">contains()</font></code>。<br /><br /></li>
								<li>
										<code>
												<font face="Courier" size="1">subject.getPublicCredentials()</font>
										</code>返回一组与 <code><font face="Courier" size="1">Subject</font></code> 相关的公用可访问凭证。<br /><br /></li>
								<li>
										<code>
												<font face="Courier" size="1">subject.getPrivateCredentials()</font>
										</code>返回一组与 <code><font face="Courier" size="1">Subject</font></code> 相关的专用可访问凭证。 </li>
						</ul>
						<p>
						</p>
						<table cellspacing="0" cellpadding="0" border="0">
								<tbody>
										<tr>
												<td width="90%">
														<font face="Verdana, Arial, Helvetica" size="4">
																<b>Principal 接口</b>
														</font>
												</td>
												<td align="right" width="200">
														<font face="Verdana, Arial, Helvetica" size="1">
														</font>
												</td>
										</tr>
								</tbody>
						</table>
						<font face="Verdana, Arial, Helvetica" size="2">
								<p>
										<code>
												<font face="Courier" size="1">Principal</font>
										</code>是一个 Java 接口。程序员编写的 <code><font face="Courier" size="1">PrincipalImpl</font></code> 对象与 <code><font face="Courier" size="1">Serializable</font></code> 接口、名称字符串、返回该字符串的 <code><font face="Courier" size="1">getName()</font></code> 方法以及其它支持方法（如 <code><font face="Courier" size="1">hashCode()</font></code>、<code><font face="Courier" size="1">toString()</font></code> 和 <code><font face="Courier" size="1">equals()</font></code>）一起实现 <code><font face="Courier" size="1">Principal</font></code> 接口。</p>
								<p>在登录过程期间，<code><font face="Courier" size="1">Principal</font></code> 被添加到 <code><font face="Courier" size="1">Subject</font></code>。正如我们稍后将看到的那样，声明性授权基于策略文件中的项。进行授权请求时，将系统的授权策略与包含在 <code><font face="Courier" size="1">Subject</font></code> 中的 <code><font face="Courier" size="1">Principal</font></code> 进行比较。如果 <code><font face="Courier" size="1">Subject</font></code> 有一个满足策略文件中安全性需求的 <code><font face="Courier" size="1">Principal</font></code>，则授权；否则拒绝。<br /><br /></p>
								<table cellspacing="0" cellpadding="0" border="0">
										<tbody>
												<tr>
														<td width="90%">
																<font face="Verdana, Arial, Helvetica" size="4">
																		<b>PrincipalImpl</b>
																</font>
														</td>
														<td align="right" width="200">
																<font face="Verdana, Arial, Helvetica" size="1">
																</font>
														</td>
												</tr>
										</tbody>
								</table>
								<br />
								<br />
								<font face="Verdana, Arial, Helvetica" size="2">
										<p>这里是我们将在本教程中使用的 <code><font face="Courier" size="1">PrincipalImpl</font></code>。</p>
										<pre>
												<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
import java.io.Serializable;
import java.security.<code><font face="Courier" size="1">Principal</font></code>;

//
// This class defines the principle object, which is just an encapsulated 
// String name 
public class PrincipalImpl implements Principal, Serializable {

     private String name;

     public PrincipalImpl(String n) {
       name = n;
     }

     public boolean equals(Object obj) {
       if (!(obj instanceof PrincipalImpl)) {
         return false;
       }
       PrincipalImpl pobj = (PrincipalImpl)obj;
       if (name.equals(pobj.getName())) {
         return true;
       }
       return false;
     }

     public String getName() {
       return name;
     }

     public int hashCode() {
       return name.hashCode();
     }

     public String toString() {
       return getName();
     }

}
<table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td width="90%"><font face="Verdana, Arial, Helvetica" size="4"><b>登录配置</b></font></td><td align="right" width="200"><font face="Verdana, Arial, Helvetica" size="1"></font></td></tr></tbody></table><br /><br /><font face="Verdana, Arial, Helvetica" size="2"><p>JAAS 允许在以下几个方面有极大的灵活性：<code><font face="Courier" size="1">Subject</font></code> 需要的认证过程种类、它们的执行顺序以及在 <code><font face="Courier" size="1">Subject</font></code> 被认为是已认证的之前要求的认证成功或失败的组合。</p><p>JAAS 使用 login.config 文件来指定每个登录模块的认证项。<code><font face="Courier" size="1">login.config</font></code> 文件是在 Java 执行命令行上用特性 <code><font face="Courier" size="1">-Djava.security.auth.login.config==login.config</font></code> 指定的。Java 有缺省登录配置文件，所以双等于号（<code><font face="Courier" size="1">==</font></code>）替换系统登录配置文件。如果使用一个等于号，login.config 文件将被添加到（而不是替换）系统登录配置文件。因为我们不知道您的系统文件中可能会有什么，所以我们这样做来确保对于各种各样的教程用户都可以得到可靠的结果。</p><p>login.config 文件包含 <code><font face="Courier" size="1">LoginContext</font></code> 构造器中引用的文本字符串和登录过程列表。几个参数用于指定一个给定的登录过程的成功或失败对总体认证过程的影响。有如下参数：</p><ul><li><code><font face="Courier" size="1">required</font></code> 表示登录模块必须成功。即使它不成功，还将调用其它登录模块。<br /><br /></li><li><code><font face="Courier" size="1">optional</font></code> 表示登录模块可以失败，但如果另一个登录模块成功，总体登录仍可以成功。如果所有登录模块都是可选的，那么要使整个认证成功至少必须有一个模块是成功的。<br /><br /></li><li><code><font face="Courier" size="1">requisite</font></code> 表示登录模块必须成功，而且如果它失败，将不调用其它登录模块。<br /><br /></li><li><code><font face="Courier" size="1">sufficient</font></code> 表示如果登录模块成功，则总体登录将成功，同时假设没有其它必需或必不可少的登录模块失败。 </li></ul><p><br /></p><p></p><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td width="90%"><font face="Verdana, Arial, Helvetica" size="4"><b>示例 login.config 文件</b></font></td><td align="right" width="200"><font face="Verdana, Arial, Helvetica" size="1"></font></td></tr></tbody></table><p><br /><br /></p><font face="Verdana, Arial, Helvetica" size="2"><p>我们将在本教程中使用的 login.config 文件如下：</p><pre><code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
JAASExample {
      AlwaysLoginModule required;
      PasswordLoginModule optional;
};
</code></pre><p>正如您看到的那样，<code><font face="Courier" size="1">AlwaysLoginModule</font></code> 必须成功，而 <code><font face="Courier" size="1">PasswordLoginModule</font></code> 可以成功也可以失败。这不是一种现实的情形，稍后我们将修改这些参数来查看不同的配置如何更改代码行为。</p><p>对于这项登录配置技术，应该认识到它将所有主要决定（如所需的认证类型和认证成功或失败的特定标准）都留到建立部署时决定，这很重要。成功的登录将导致新的 <code><font face="Courier" size="1">Subject</font></code> 添加到 <code><font face="Courier" size="1">LoginContext</font></code>，同时将所有成功认证的 <code><font face="Courier" size="1">Principal</font></code> 添加到该 <code><font face="Courier" size="1">Subject</font></code>。</p><p><br /></p><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td width="90%"><font face="Verdana, Arial, Helvetica" size="4"><b>登录环境</b></font></td><td align="right" width="200"><font face="Verdana, Arial, Helvetica" size="1"></font></td></tr></tbody></table><br /><br /><font face="Verdana, Arial, Helvetica" size="2"><p><code><font face="Courier" size="1">LoginContext</font></code> 是一种用于设置登录过程的 Java 类，它进行实际的登录，如果登录成功，获取 <code><font face="Courier" size="1">Subject</font></code>。它有如下四种主要方法：</p><ul><li><code><font face="Courier" size="1">LoginContext("JAASExample", newUsernamePasswoerdCallbackHandler())</font></code> 是构造器。它把 login.config 文件中使用的字符串作为其第一个参数，把执行实际任务的回调处理程序作为其第二个参数。（接下来，我们将讨论回调处理程序。）<br /><br /></li><li><code><font face="Courier" size="1">login()</font></code>，它根据 login.config 文件中指定的规则实际尝试登录。<br /><br /></li><li><code><font face="Courier" size="1">getSubject()</font></code>，如果登录总体成功，它返回经认证的 <code><font face="Courier" size="1">Subject</font></code>。<br /><br /></li><li><code><font face="Courier" size="1">logout()</font></code>，它向 <code><font face="Courier" size="1">LoginContext</font></code> 注销 <code><font face="Courier" size="1">Subject</font></code>。 </li></ul><p><br /><br /></p><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td width="90%"><font face="Verdana, Arial, Helvetica" size="4"><b>回调处理程序</b></font></td><td align="right" width="200"><font face="Verdana, Arial, Helvetica" size="1"></font></td></tr></tbody></table><br /><br /><font face="Verdana, Arial, Helvetica" size="2"><p>JAAS 登录使用回调处理程序来获取用户的认证信息。<code><font face="Courier" size="1">CallbackHandler</font></code> 是在 <code><font face="Courier" size="1">LoginContext</font></code> 对象的构造函数中指定的。在本教程中，回调处理程序使用几个提示来获取用户的用户名和密码信息。从登录模块调用的处理程序的 <code><font face="Courier" size="1">handle()</font></code> 方法将 <code><font face="Courier" size="1">Callback</font></code> 数组对象作为其参数。在登录期间，处理程序遍历 <code><font face="Courier" size="1">Callback</font></code> 数组。<code><font face="Courier" size="1">handle()</font></code> 方法检查 <code><font face="Courier" size="1">Callback</font></code> 对象的类型并执行适当的用户操作。<code><font face="Courier" size="1">Callback</font></code> 类型如下：</p><ul><li><code><font face="Courier" size="1">NameCallback</font></code></li><li><code><font face="Courier" size="1">PasswordCallback</font></code></li><li><code><font face="Courier" size="1">TextInputCallback</font></code></li><li><code><font face="Courier" size="1">TextOutputCallback</font></code></li><li><code><font face="Courier" size="1">LanguageCallback</font></code></li><li><code><font face="Courier" size="1">ChoiceCallback</font></code></li><li><code><font face="Courier" size="1">ConfirmationCallback</font></code></li></ul><p>在某些应用程序中，因为 JAAS 将用于与操作系统的认证机制相互操作，所以不需要任何用户交互。在这种情况下，<code><font face="Courier" size="1">LoginContext</font></code> 对象中的 <code><font face="Courier" size="1">CallbackHandler</font></code> 参数将是空的。<br /><br /><br /><br /><br /><br /><br /><br /><br /></p></font></font></font></font></code>
												<table cellspacing="0" cellpadding="0" border="0">
														<tbody>
																<tr>
																		<td width="90%">
																				<font face="Verdana, Arial, Helvetica" size="4">
																						<b>回调处理程序代码</b>
																				</font>
																		</td>
																		<td align="right" width="200">
																		</td>
																</tr>
														</tbody>
												</table>
												<br />
												<br />
												<font face="Verdana, Arial, Helvetica" size="2">
														<p>下面是本教程中使用的 <code><font face="Courier" size="1">UsernamePasswordCallbackHandler</font></code> 的代码。它由 <code><font face="Courier" size="1">AlwaysLoginModule</font></code> 调用一次（仅一次回调以获取用户标识），由 <code><font face="Courier" size="1">PasswordLoginModule</font></code> 调用一次（两次回调以获取用户标识和密码）。</p>
														<pre>
																<code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
import java.io.*;
import java.security.*;
import javax.security.auth.*;
import javax.security.auth.callback.*;
//
// This class implements a username/password callback handler that gets 
// information from the user public class
UsernamePasswordCallbackHandler implements CallbackHandler {
     //
     // The handle method does all the work and iterates through the array
     // of callbacks, examines the type, and takes the appropriate user
     // interaction action.
     public void handle(Callback[] callbacks) throws
         UnsupportedCallbackException, IOException {

       for(int i=0;i&amp;lt;callbacks.length;i++) {
         Callback cb = callbacks[i];
         //
         // Handle username aquisition
         if (cb instanceof NameCallback) {
           NameCallback nameCallback = (NameCallback)cb;
           System.out.print( nameCallback.getPrompt() + "? ");
           System.out.flush();
           String username = new BufferedReader(
               new InputStreamReader(System.in)).readLine();
           nameCallback.setName(username);
           //
           // Handle password aquisition
         } else if (cb instanceof PasswordCallback) {
           PasswordCallback passwordCallback = (PasswordCallback)cb;
           System.out.print( passwordCallback.getPrompt() + "? ");
           System.out.flush();
           String password = new BufferedReader(
               new InputStreamReader(System.in)).readLine();
           passwordCallback.setPassword(password.toCharArray());
           password = null;
           //
           // Other callback types are not handled here
         } else {
           throw new UnsupportedCallbackException(cb, "Unsupported 
Callback Type");
         }
       }
     }
}<br /><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td width="90%"><font face="Verdana, Arial, Helvetica" size="4"><b>登录模块</b></font></td><td align="right" width="200"></td></tr></tbody></table><br /><br /><font face="Verdana, Arial, Helvetica" size="2"><p><code><font face="Courier" size="1">LoginModule</font></code> 是参与 JAAS 认证过程所需的方法的接口。因为可能要到执行其它登录过程时才知道特定登录过程是成功还是失败，所以用两阶段提交过程来确定是否成功。下列方法由 <code><font face="Courier" size="1">LoginModule</font></code> 对象实现：</p><ul><li><code><font face="Courier" size="1">initialize( subject, callbackHandler, sharedState, options)</font></code> 初始化 <code><font face="Courier" size="1">LoginModule</font></code>。（注：对 <code><font face="Courier" size="1">sharedState</font></code> 和 <code><font face="Courier" size="1">options</font></code> 的讨论超出了本教程的范围。）<br /><br /></li><li><code><font face="Courier" size="1">login()</font></code> 设置任何必需的回调，调用 <code><font face="Courier" size="1">CallbackHandler</font></code> 来处理它们，并将返回的信息（即用户名和密码）与允许值进行比较。如果匹配，则登录模块成功，尽管仍可能因为另一个登录模块不成功而异常终止它，这取决于 login.config 文件中的设置。<br /><br /></li><li><code><font face="Courier" size="1">commit()</font></code> 作为两阶段提交过程的一部分被调用以确定是否成功。如果根据 login.config 文件中指定的约束，所有登录模块都是成功的，那么新的 <code><font face="Courier" size="1">Principal</font></code> 随同用户名一起创建，并被添加到 <code><font face="Courier" size="1">Subject</font></code> 的主体集。<br /><br /></li><li><code><font face="Courier" size="1">abort()</font></code>，如果总体登录未成功，则调用它；如果发生异常终止，必须清除内部的 <code><font face="Courier" size="1">LoginModule</font></code> 状态。<br /><br /></li><li><code><font face="Courier" size="1">logout()</font></code> 被调用以除去 <code><font face="Courier" size="1">Subject</font></code> 的主体集中的 <code><font face="Courier" size="1">Principal</font></code> 并执行其它内部状态清除。 </li></ul><p>下面两页说明了两个登录模块。第一个是 <code><font face="Courier" size="1">AlwaysLoginModule</font></code>，它始终是成功的。第二个是 <code><font face="Courier" size="1">PasswordLoginModule</font></code>，仅当用户标识和密码与某些硬编码值匹配时，它才会成功。虽然两个示例模块都不是合乎实际的实现，但它们共同演示了各种 JAAS 选项的结果。<br /><br /></p><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td width="90%"><font face="Verdana, Arial, Helvetica" size="4"><b>AlwaysLoginModule</b></font></td><td align="right" width="200"><font face="Verdana, Arial, Helvetica" size="1"></font></td></tr></tbody></table><br /><br /><font face="Verdana, Arial, Helvetica" size="2"><p><code><font face="Courier" size="1">AlwaysLoginModule</font></code> 认证将始终成功，所以实际上它仅用于通过 <code><font face="Courier" size="1">NameCallback</font></code> 函数获取用户名。假设其它登录模块都成功，<code><font face="Courier" size="1">AlwaysLoginModule</font></code> 的 <code><font face="Courier" size="1">commit()</font></code> 方法将创建一个带用户名的新 <code><font face="Courier" size="1">PrincipalImpl</font></code> 对象并将它添加到 <code><font face="Courier" size="1">Subject</font></code> 的 <code><font face="Courier" size="1">Principal</font></code> 集中。注销将除去 <code><font face="Courier" size="1">Subject</font></code> 的 <code><font face="Courier" size="1">Principal</font></code> 集中的 <code><font face="Courier" size="1">PrincipalImpl</font></code>。</p><pre><code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
import java.security.*;
import javax.security.auth.*;
import javax.security.auth.spi.*;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import java.io.*;
import java.util.*;

// This is a JAAS Login Module that always succeeds.  While not realistic, 
// it is designed to illustrate the bare bones structure of a Login Module 
// and is used in examples that show the login configuration file 
// operation.

public class AlwaysLoginModule implements LoginModule {

     private <code><font face="Courier" size="1">Subject</font></code> subject;
     private <code><font face="Courier" size="1">Principal</font></code> principal;
     private CallbackHandler callbackHandler;
     private String username;
     private boolean loginSuccess;
     //
     // Initialize sets up the login module.  sharedState and options are
     // advanced features not used here
     public void initialize(<code><font face="Courier" size="1">Subject</font></code> sub, CallbackHandler cbh,
       Map sharedState, Map options) {

       subject = sub;
       callbackHandler = cbh;
       loginSuccess = false;
     }
     //
     // The login phase gets the userid from the user
     public boolean login() throws LoginException {
       //
       // Since we need input from a user, we need a callback handler
       if (callbackHandler == null) {
         throw new LoginException( "No CallbackHandler defined");

       }
       Callback[] callbacks = new Callback[1];
       callbacks[0] = new NameCallback("Username");
       //
       // Call the callback handler to get the username
       try {
         System.out.println( "\nAlwaysLoginModule Login" );
         callbackHandler.handle(callbacks);
         username = ((NameCallback)callbacks[0]).getName();
       } catch (IOException ioe) {
         throw new LoginException(ioe.toString());
       } catch (UnsupportedCallbackException uce) {
         throw new LoginException(uce.toString());
       }
       loginSuccess = true;
       System.out.println();
       System.out.println( "Login: AlwaysLoginModule SUCCESS" );
       return true;
     }
     //
     // The commit phase adds the principal if both the overall authentication
     // succeeds (which is why commit was called) as well as this particular
     // login module
     public boolean commit() throws LoginException {
       //
       // Check to see if this login module succeeded (which it always will

       // in this example)
       if (loginSuccess == false) {
         System.out.println( "Commit: AlwaysLoginModule FAIL" );
         return false;
       }
       //
       // If this login module succeeded too, then add the new principal
       // to the subject (if it does not already exist)
       principal = new PrincipalImpl(username);
       if (!(subject.getPrincipals().contains(principal))) {
         subject.getPrincipals().add(principal);
       }
       System.out.println( "Commit: AlwaysLoginModule SUCCESS" );
       return true;
     }
     //
     // The abort phase is called if the overall authentication fails, so
     // we have to clean up the internal state
     public boolean abort() throws LoginException {

       if (loginSuccess == false) {
         System.out.println( "Abort: AlwaysLoginModule FAIL" );
         principal = null;
         return false;
       }
       System.out.println( "Abort: AlwaysLoginModule SUCCESS" );
       logout();

       return true;
     }
     //
     // The logout phase cleans up the state
     public boolean logout() throws LoginException {

       subject.getPrincipals().remove(principal);
       loginSuccess = false;
       principal = null;
       System.out.println( "Logout: AlwaysLoginModule SUCCESS" );
       return true;
      }
}
</code><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td width="90%"><font face="Verdana, Arial, Helvetica" size="4"><b>PasswordLoginModule</b></font></td><td align="right" width="200"></td></tr></tbody></table><br /><br /><font face="Verdana, Arial, Helvetica" size="2"><p><code><font face="Courier" size="1">PasswordLoginModule</font></code> 使用 <code><font face="Courier" size="1">NameCallback</font></code> 来获取用户名并使用 <code><font face="Courier" size="1">PasswordCallback</font></code> 来获取密码。如果用户名是“joeuser”，密码是“joe”，则该认证将成功。</p><pre><code style="FONT-SIZE: 12px; FONT-FAMILY: Courier New, Courier, monospace">
import java.security.*;
import javax.security.auth.*;
import javax.security.auth.spi.*;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import java.io.*;
import java.util.*;
//
// This is a JAAS Login Module that requires both a username and a  
// password. The username must equal the hardcoded "joeuser" and 
// the password must match the hardcoded "joeuserpw". 
public class
PasswordLoginModule implements LoginModule {

     private <code><font face="Courier" size="1">Subject</font></code> subject;
     private <code><font face="Courier" size="1">Principal</font></code> principal;
     private CallbackHandler callbackHandler;
     private String username;
     private char[] password;
     private boolean loginSuccess;
     //
     // Initialize sets up the login module.  sharedState and options are
     // advanced features not used here
     public void initialize(<code><font face="Courier" size="1">Subject</font></code> sub, CallbackHandler cbh,
       Map sharedState,Map options) {

       subject = sub;
       callbackHandler = cbh;
       loginSuccess = false;
       username = null;
       clearPassword();
     }
     //
     // The login phase gets the userid and password from the user and
     // compares them to the hardcoded values "joeuser" and "joeuserpw".
     public boolean login() throws LoginException {
       //
       // Since we need input from a user, we need a callback handler
       if (callbackHandler == null) {
          throw new LoginException("No CallbackHandler defined");
       }
       Callback[] callbacks = new Callback[2];
       callbacks[0] = new NameCallback("Username");
       callbacks[1] = new PasswordCallback("Password", false);
       //
       // Call the callback handler to get the username and password
       try {
         System.out.println( "\nPasswordLoginModule Login" );
         callbackHandler.handle(callbacks);
         username = ((NameCallback)callbacks[0]).getName();
         char[] temp = ((PasswordCallback)callbacks[1]).getPassword();
         password = new char[temp.length];
         System.arraycopy(temp, 0, password, 0, temp.length);
         ((PasswordCallback)callbacks[1]).clearPassword();
       } catch (IOException ioe) {
         throw new LoginException(ioe.toString());
       } catch (UnsupportedCallbackException uce) {
         throw new LoginException(uce.toString());
       }
       System.out.println();
       //
       // If username matches, go on to check password
       if ( "joeuser".equals(username)) {
         System.out.println
           ( "Login: PasswordLoginModule Username Matches" );
         if ( password.length == 5 &amp;&amp;
             password[0] == 'j' &amp;&amp;
             password[1] == 'o' &amp;&amp;
             password[2] == 'e' &amp;&amp;
             password[3] == 'p' &amp;&amp;
             password[4] == 'w' ) {
           //
           //If userid and password match, then login is a success
           System.out.println
             ( "Login: PasswordLoginModule Password Matches" );
           loginSuccess = true;
           System.out.println
             ( "Login: PasswordLoginModule SUCCESS" );
           clearPassword();
           return true;
         } else {
           System.out.println
            ( "Login: PasswordLoginModule Password Mismatch" );
         }
       } else {
         System.out.println( "Login: PasswordLoginModule Username Mismatch" );
       }
       //
       // If either mismatch, then this login module fails

       loginSuccess = false;
       username = null;
       clearPassword();
       System.out.println( "Login: PasswordLoginModule FAIL" );
       throw new FailedLoginException();
     }
     //
     // The commit phase adds the principal if both the overall 
     // authentication succeeds (which is why commit was called) 
     // as well as this particular login module
     public boolean commit() throws LoginException {

       //
       // Check to see if this login module succeeded
       if (loginSuccess == false) {
         System.out.println( "Commit: PasswordLoginModule FAIL" );
         return false;
       }
       // If this login module succeeded too, then add the new principal
       // to the subject (if it does not already exist)
       principal = new PrincipalImpl(username);
       if (!(subject.getPrincipals().contains(principal))) {
         subject.getPrincipals().add(principal);
       }
       username = null;
       System.out.println( "Commit: PasswordLoginModule SUCCESS" );
       return true;
     }
     //
     // The abort phase is called if the overall authentication fails, so
     // we have to cleanup the internal state
     public boolean abort() throws LoginException {

       if (loginSuccess == false) {
         System.out.println( "Abort: PasswordLoginModule FAIL" );
         principal = null;
         username = null;
         return false;
       }
       System.out.println( "Abort: PasswordLoginModule SUCCESS" );
       logout();
       return true;
     }
     //
     // The logout phase cleans up the state
     public boolean logout() throws LoginException {
       subject.getPrincipals().remove(principal);
       loginSuccess = false;
       username = null;
       principal = null;
       System.out.println( "Logout: PasswordLoginModule SUCCESS" );
       return true;
     }
     //
     // Private helper function to clear the password, a good programming
     // practice
     private void clearPassword() {
       if (password == null) {
         return;
       }
       for (int i=0;i&lt;password.length;i++) {
         password[i] = ' ';
       }
       password = null;
     }
}
</code><br /><br /></pre></font></pre></font></font></code>
														</pre>
												</font>
										</pre>
								</font>
						</font>
				</font>
		</font>
<img src ="http://www.blogjava.net/javaniu/aggbug/61098.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/javaniu/" target="_blank">Gary Niu</a> 2006-07-31 23:35 <a href="http://www.blogjava.net/javaniu/archive/2006/07/31/61098.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>