Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
如果 The Security Manager 生效,则必须满足以下条件才能启用任何软件(包括扩展软件)执行安全敏感操作:
PrivilegedAction
对象中。PrivilegedAction
实例中包含的代码。让我们通过一些例子更详细地看一下这些条件。
假设你要修改上一课的扩展示例中的 RectangleArea
类,以将矩形面积写入文件而不是 stdout。但是,写入文件是一项对安全性敏感的操作,因此如果你的软件将在安全管理器下运行,则需要将你的代码标记为特权。你需要执行以下两个步骤:
java.security.PrivilegedAction
类型的对象的 run
方法中放置执行安全敏感操作的代码。PrivilegedAction
对象作为调用 java.security.AccessController
的 doPrivileged
方法的参数。如果我们将这些指南应用于 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
,计算矩形面积,并将该面积写入用户主目录下的 test
目录中的名为 area.txt
的文件中。
处理输出文件的安全敏感语句位于 PrivilegedAction
的新实例的 run
方法中。(注意 run
要求返回 Object
实例。返回的对象可以是 null
。)然后,新的 PrivilegedAction
实例作为参数传递给 AccessController.doPrivileged
。
有关使用 doPrivileged
的更多信息,请参阅 JDK™ 文档中的 API for Privileged Blocks。
以这种方式在 PrivilegedAction
对象中包装安全敏感代码是启用扩展以执行安全敏感操作的第一个要求。第二个要求是:让安全管理器授予特权代码适当的权限。
运行时生效的安全策略由 policy file (策略文件) 指定。默认策略由 JRE 软件中的文件 lib/security/java.policy
设置。
策略文件使用 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
的实例中。
要限制授予扩展的权限,你需要修改策略文件。要拒绝所有扩展的所有权限,你只需移除上述授权条目即可。
并非所有权限都与默认授予的 java.security.AllPermission
一样全面。删除默认授权条目后,你可以输入特定权限的新授权条目,包括:
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
Permissions in the 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 文件的回顾,请参见本教程中的 Signing JAR Files 课程。)
要允许签名验证扩展或其他软件以及授予权限,策略文件必须包含 keystore entry (密钥库条目)。keystore 条目指定在验证中使用哪个密钥库。密钥库条目具有表单
keystore "keystore_url";
URL keystore_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 节。