本教程适用于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.AWTPermissionjava.io.FilePermissionjava.net.NetPermissionjava.util.PropertyPermissionjava.lang.reflect.ReflectPermissionjava.lang.RuntimePermissionjava.security.SecurityPermissionjava.io.SerializablePermissionjava.net.SocketPermissionJDK文档中的权限提供了每个权限的详细信息。让我们看看作为扩展使用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节。