本教程适用于JDK 8。本页面中描述的示例和实践未利用后续版本中引入的改进,并可能使用不再可用的技术。
请参阅Java语言更改了解Java SE 9及后续版本中更新的语言特性的摘要。
请参阅JDK发行说明了解所有JDK版本的新功能、增强功能以及已删除或弃用选项的信息。
如果启用了安全管理器,则必须满足以下条件,以便使任何软件(包括扩展软件)能够执行安全敏感操作:
PrivilegedAction
对象中。PrivilegedAction
实例中的代码。让我们稍微详细介绍一下这些条件,并提供一些示例。
假设您想要修改上一课程示例中的扩展中的RectangleArea
类,将矩形面积写入文件而不是标准输出。然而,写入文件是一个安全敏感操作,所以如果您的软件将在安全管理器下运行,您需要将代码标记为特权代码。您需要执行两个步骤:
java.security.PrivilegedAction
的对象的run
方法中。java.security.AccessController
的doPrivileged
方法的参数来使用该PrivilegedAction
对象。如果我们将这些指南应用于RectangleArea
类,我们的类定义将如下所示:
import java.io.*; import java.security.*; public final class RectangleArea { public static void writeArea(final java.awt.Rectangle r) { AccessController. doPrivileged(new PrivilegedAction() { public Object run() { try { int area = r.width * r.height; String userHome = System.getProperty("user.home"); FileWriter fw = new FileWriter( userHome + File.separator + "test" + File.separator + "area.txt"); fw.write("The rectangle's area is " + area); fw.flush(); fw.close(); } catch(IOException ioe) { System.err.println(ioe); } return null; } }); } }
这个类中的唯一方法writeArea
计算一个矩形的面积,并将面积写入名为area.txt
的文件中,该文件位于用户主目录下的test
目录中。
处理输出文件的安全敏感语句被放置在新的PrivilegedAction
实例的run
方法中。(注意,run
要求返回一个Object
实例。返回的对象可以是null
。)然后将新的PrivilegedAction
实例作为参数传递给AccessController.doPrivileged
的调用中。
有关使用doPrivileged
的更多信息,请参阅JDK™文档中的特权块的API。
将安全敏感代码包装在PrivilegedAction
对象中是启用扩展执行安全敏感操作的第一个要求。第二个要求是:获取安全管理器授予特权代码适当权限。
运行时生效的安全策略由一个策略文件指定。默认策略由JRE软件中的文件lib/security/java.policy
设置。
策略文件通过使用grant条目为软件分配安全权限。策略文件可以包含任意数量的grant条目。默认策略文件中为已安装的扩展有以下grant条目:
grant codeBase "file:${{java.ext.dirs}}/*" { permission java.security.AllPermission; };
该条目指定file:${{java.ext.dirs}}/*
目录中的文件将被授予名为java.security.AllPermission
的权限。(请注意,从Java 6开始,java.ext.dirs
指的是类似于classpath的目录路径,每个目录可以包含已安装的扩展。)可以猜测java.security.AllPermission
授予已安装的扩展所有可能的安全权限。
因此,默认情况下,已安装的扩展没有安全限制。扩展软件可以执行安全敏感操作,就好像没有安装安全管理器一样,前提是安全敏感代码包含在作为doPrivileged
调用的参数中的PrivilegedAction
实例中。
要限制授予扩展的权限,需要修改策略文件。要拒绝所有扩展的所有权限,可以简单地删除上述grant条目。
并非所有权限都像默认情况下授予的java.security.AllPermission
一样全面。在删除默认的grant条目后,可以为特定权限输入新的grant条目,包括:
java.awt.AWTPermission
java.io.FilePermission
java.net.NetPermission
java.util.PropertyPermission
java.lang.reflect.ReflectPermission
java.lang.RuntimePermission
java.security.SecurityPermission
java.io.SerializablePermission
java.net.SocketPermission
JDK文档中的权限提供了每个权限的详细信息。让我们看看作为扩展使用RectangleArea所需的权限。
RectangleArea.writeArea
方法需要两个权限:一个是确定用户主目录路径的权限,另一个是写入文件的权限。假设RectangleArea
类被打包在area.jar
文件中,您可以通过向策略文件添加以下条目来授予写入权限:
grant codeBase "file:${java.home}/lib/ext/area.jar" { permission java.io.PropertyPermission "user.home", "read"; permission java.io.FilePermission "${user.home}${/}test${/}*", "write"; };
此条目中的codeBase "file:${java.home}/lib/ext/area.jar"
部分确保此条目指定的权限仅适用于area.jar
。java.io.PropertyPermission
允许访问属性。第一个参数"user.home"
指定属性名称,第二个参数"read"
表示可以读取该属性。(另一个选择是"write"
)
java.io.FilePermission
允许访问文件。第一个参数"${user.home}${/}test${/}*"
表示area.jar
被授予访问用户主目录中的test
目录下所有文件的权限。(注意,${/}
是一个与平台无关的文件分隔符)第二个参数表示授予的文件访问权限仅限于写入。(第二个参数的其他选择包括"read"
、"delete"
和"execute"
)
您可以使用策略文件对扩展程序授予的权限进行额外限制,要求扩展程序必须由可信实体签名。(有关签名和验证JAR文件的概述,请参见本教程中的签署JAR文件课程)
要在授予权限的同时允许扩展程序或其他软件进行签名验证,策略文件必须包含一个密钥库条目。密钥库条目指定要在验证中使用的密钥库。密钥库条目的格式如下:
keystore "keystore_url";
keystore_url是绝对或相对URL。如果是相对URL,则相对于策略文件的位置。例如,要使用keytool使用的默认密钥库,将以下条目添加到java.policy中:
keystore "file://${user.home}/.keystore";
要指示扩展程序必须签名才能被授予安全权限,您可以使用signedBy
字段。例如,以下条目指示只有在area.jar
被用户别名为Robert和Rita的用户签名时,才授予该扩展程序所列出的权限:
grant signedBy "Robert,Rita", codeBase "file:${java.home}/lib/ext/area.jar" { permission java.io.PropertyPermission "user.home", "read"; permission java.io.FilePermission "${user.home}${/}test${/}*", "write"; };
如果省略codeBase
字段,如下所示的"grant",权限将授予由"Robert"或"Rita"签名的任何软件,包括已安装或下载的扩展软件:
grant signedBy "Robert,Rita" { permission java.io.FilePermission "*", "write"; };
有关策略文件格式的详细信息,请参阅JDK文档中的Security Architecture Specification的3.3.1节。