Java 应用程序环境的策略(对不同来源的代码指定权限)由 Policy 对象来表示。更明确地说,就是由 Policy 类(包含在 java.security 包中)的实现抽象方法的 Policy 子类来表示。
Policy 对象所用策略信息的源位置由 Policy 实现决定。缺省 Policy 实现从静态策略配置文件获得自己的信息。本文档的其余部分叙述了缺省 Policy 实现及其所读取的策略文件中必须使用的语法。有关使用 Policy Tool 来创建策略文件(不必知道所需语法)的详细信息,请参阅《策略工具文档》 (for Solaris) (for Windows)。
以下是本文档其余部分的概要:
在缺省 Policy 实现中,可在一个或多个策略配置文件中指定策略。配置文件的作用是指定特定代码源的代码所能获得的权限。
可利用简单的文本编辑器或 Policy Tool 实用程序来编写策略文件。
缺省情况下,系统上只有单个全系统策略文件和唯一的(可选)用户策略文件。
首次调用缺省 Policy 对象的 getPermissions 方法或在任何时候调用 Policy 对象 refresh 方法时,即对其进行初始化。初始化包括分析策略配置文件(请参阅策略文件语法)及组装 Policy 对象。
如前所述,系统在缺省情况下具有单个全系统策略文件和唯一的用户策略文件。
系统策略文件的缺省位置为:
java.home/lib/security/java.policy (Solaris) java.home\lib\security\java.policy (Windows)注意: java.home 指的是名为“java.home”的系统属性的值,它指定 JDK 的安装目录。
系统策略文件可用于授予全系统代码权限。与 JDK 一起安装的 java.policy 文件可向标准扩展 (Java standard extensions) 授予全部权限,允许任何用户在无特权要求的端口进行监听,同时允许任何代码读取某些对安全不敏感的“标准”属性(例如“os.name”和“file.separator”属性)。
用户策略文件的缺省位置为:
user.home/.java.policy (Solaris) user.home\.java.policy (Windows)注意: user.home 指的是名为“user.home”的系统属性的值,它指定用户的主目录。在 Windows 系统中,假定用户名是 uName,“user.home”属性的缺省值为:
C:\Winnt\Profiles\uName(多用户 Windows NT 系统中) C:\Windows\Profiles\uName(多用户 Windows 95 系统中) C:\Windows(单用户 Windows 95 系统中)初始化 Policy 时,将首先加载系统策略,然后在 Policy 中添加用户策略。如果两种策略均不存在,则采用内置策略。该内置策略与原始的沙箱策略相同。
策略文件的位置在安全属性文件中指定。安全属性文件的位置为:
java.home/lib/security/java.security (Solaris) java.home\lib\security\java.security (Windows)如上所述,java.home 指示 JDK 的安装目录。策略文件的位置被指定为其名称具有以下形式的属性的值:
policy.url.n其中 n 为数字。应采用以下形式的语句行来指定每个属性值:
policy.url.n=URL其中,URL 为 URL 规范。
例如,安全属性文件中将把缺省系统和用户策略文件定义为:
policy.url.1=file:${java.home}/lib/security/java.policy policy.url.2=file:${user.home}/.java.policy有关利用特殊语法(例如利用 ${java.home} 来指定 java.home 属性值)来指定属性值的详细信息,请参阅属性扩展。
实际上,用户可以指定多个 URL(包括“http://”形式的 URL),从而加载所有指定的策略文件。也可注释掉或更改第二个 URL,从而禁止读取缺省用户策略文件。
该算法自 policy.url.1 开始,然后不断递增直到查不到 URL 为止。因此,如果有了 policy.url.1 和 policy.url.3,就不会读取 policy.url.3。
运行时指定其它策略文件
在执行应用程序时也可以指定附加的或不同的策略文件,方法是用“-Djava.security.policy”命令行参数来指定(该命令行参数设置 java.security.policy 属性值)。例如,如果使用
java -Djava.security.manager -Djava.security.policy=someURL SomeApp这里 someURL 是指定策略文件位置的 URL,则除了加载安全属性文件中指定的所有策略文件外,还会加载本方法所指定的策略文件。
注意:
- URL 可以是任何标准 URL,也可以只是当前目录下策略文件的文件名,如下例所示:
java -Djava.security.manager -Djava.security.policy=mypolicy WriteFile- “-Djava.security.manager”参数可确保缺省安全管理器已被安装,这样就容易对应用程序进行策略检查。如果应用程序 SomeApp 安装有安全管理器,则不需要该参数。
如果使用
java -Djava.security.manager -Djava.security.policy==someURL SomeApp(请注意双等号),就会仅使用指定的策略文件,而安全属性文件中指出的策略文件将被忽略。
如果要将策略文件传递给 appletviewer,就应使用参数“-J-Djava.security.policy”,如下所示:
appletviewer -J-Djava.security.policy=someURL myApplet请注意:如果将安全属性文件中的“policy.allowSystemProperty”属性设置为“false”,就会忽略“-Djava.security.policy”策略文件值(对于 java 和 appletviewer 命令)。缺省值为“true”。
可以用其它 policy 类来代替缺省 Policy 实现类,前提是前者属于抽象 Policy 类的子类并可实现 getPermissions 方法(及其它必要的方法)。
缺省 Policy 实现的更改可通过编辑安全属性文件来完成,其中安全属性文件指 JDK lib/security 目录中的 java.security 文件。
下面给出一种可在 java.security 中设置的属性类型的形式:
policy.provider=PolicyClassNamePolicyClassName 必须指定所需 Policy 实现类的完整名称。该属性的缺省安全属性文件项如下所示:
policy.provider=sun.security.provider.PolicyFile要想自定义安全属性文件项,可通过更改属性值来指定另一个类,如下例所示:
policy.provider=com.mycom.MyPolicy
JDK 的策略配置文件可用于指定来自特定代码源的代码所能获得的权限(何种系统资源访问类型)。
为了使 applet(或在安全管理器下运行的应用程序)能够执行受保护的动作(例如读写文件),必须向 applet(或应用程序)授予进行该动作的权限。在缺省 Policy 实现中,必须由策略配置文件中的 grant 项授予该权限。有关详细信息,请参阅以下内容及 “Java 安全体系结构规范”(唯一的例外是:代码对位于与它自身同一 (URL) 位置并且对那一位置子目录下的文件总是自动拥有读权限,而无需授予明确的权限)。
策略配置文件主要包含授权项列表。其中可能包含“keystore”(密钥仓库)项及零个或多个“grant”(授权)项。
Keystore 项
keystore 是存放私钥及相关数字证书(例如验证对应的公钥的 X.509 证书链)的数据库。keytool 实用程序 (for Solaris) (for Windows) 用于创建和管理密钥仓库。策略配置文件中所指定的 keystore 用于查找在该文件的授权项中所指定的签名人公钥。如果某一授权项指定了签名人别名(请参阅以下内容),则在策略配置文件中必须含有 keystore 项。
目前,在策略文件中只能有一个 keystore 项(第一项后的其它 keystore 项将被忽略),且该项可位于文件授权项以外的任何位置。其语法如下所示:
keystore "some_keystore_url", "keystore_type";其中“some_keystore_url”指定密钥仓库的 URL 位置,而“keystore_type”指定密钥仓库的类型。
URL 是相对于策略文件位置而言。因此,如果在安全属性文件中按以下方式指定策略文件:
policy.url.1=http://foo.bar.com/fum/some.policy而且策略文件中含有以下项:
keystore ".keystore";就会从下列位置加载密钥仓库:
http://foo.bar.com/fum/.keystoreURL 也可以是绝对 URL。
keystore type 定义密钥仓库信息的存储和数据格式,同时也定义用于保护密钥仓库中私钥及密钥仓库自身完整性的算法。Sun Microsystems 所支持的缺省类型是名为“JKS”的专用密钥仓库类型。因此,如果密钥仓库类型属于“JKS”,就无需在 keystore 项中加以指定。
授权项
通常认为执行代码来自于某“代码源”(由 CodeSource 类型的对象表示)。代码源不仅包含代码的源位置 (URL),而且还包括对包含与签写代码的私钥相对应的公钥的证书之引用。代码源中的证书通过用户密钥仓库中的符号别名引用。
每个授权项包括一个或多个“权限项”,前面为可选 codeBase 和 signedBy 名字/值对,用于指定要授予权限的代码。授权项的基本格式如下所示:
grant signedBy "signer_names", codeBase "URL" { permission permission_class_name "target_name", "action", signedBy "signer_names"; .... permission permission_class_name "target_name", "action", signedBy "signer_names"; };以上所有非斜体的项必须按原样出现(尽管大小写无关紧要且部分为可选项,如下所示)。 斜体项代表变量值。
授权项必须以 grant 开头。
SignedBy 和 CodeBase 域
signedBy 和 codeBase 名字/值对为可选域,其间的顺序无关紧要。
signedBy 值表示存储在密钥仓库中的证书别名。该证书内的公钥用于验证代码上的数字签名;用户可以向由私钥(私钥对应于该别名所指定的 keystore 项中的公钥)签名的代码授予权限。
signedBy 的值可以是由逗号分隔的多个别名。 例如“Adam,Eve,Charles”,其含义为“Adam,Eve 和 Charles 签名”;它们之间的关系是 AND(与)而非 OR(或)。更确切地说,“Adam 签名的代码”语句的含义是“JAR 文件中有含类文件的代码,这个 JAR 文件已用密钥仓库中别名为 Adam 的项中与公钥所对应的私钥签名”。
signedBy 域可选,这是因为如果省略该域,则表示“任何签名人”。代码是否有签名或由谁签名都没有关系。
codeBase 值表示的是代码源位置;用户可向来自该位置的代码授权。空 codeBase 项表示“任何代码”;代码来源于何处没有关系。
注意: codeBase 值是 URL,因此应该始终用正斜杠(而不要用反斜杠)作为目录分隔符,即使代码源实际在 Windows 系统上。这样,如果 Windows 系统上代码的源位置实际上是 C:\somepath\api\,则 codeBase 策略项的外观将如下所示:
grant codeBase "file:/C:/somepath/api/" { ... }codeBase
值的准确含义要取决于最后的字符。后面跟着“/”的 codeBase 将匹配指定目录下的所有类文件(非 JAR 文件)。后面跟着“/*”的 codeBase 将匹配该目录下的所有文件(类文件和 JAR 文件)。后面跟着“/-”的 codeBase 将匹配该目录下的所有文件(类文件和 JAR 文件)及该目录下子目录中的所有文件。下表说明了各种不同的情况。
下载代码的 Codebase URL 策略中的 Codebase URL 是否匹配? java.sun.com/people/gong/ java.sun.com/people/gong 是
java.sun.com/people/gong/ java.sun.com/people/gong/ 是
java.sun.com/people/gong/ java.sun.com/people/gong/* 是
java.sun.com/people/gong/ java.sun.com/people/gong/- 是
java.sun.com/people/gong/appl.jar java.sun.com/people/gong/ 否
java.sun.com/people/gong/appl.jar java.sun.com/people/gong/- 是
java.sun.com/people/gong/appl.jar java.sun.com/people/gong/* 是
java.sun.com/people/gong/appl.jar java.sun.com/people/- 是
java.sun.com/people/gong/appl.jar java.sun.com/people/* 否
java.sun.com/people/gong/ java.sun.com/people/- 是
java.sun.com/people/gong/ java.sun.com/people/* 否
权限项
权限项必须以 permission 开头。上述模板中的字 permission_class_name 的实际值可以是特定的权限类型(例如 java.io.FilePermission 或 java.lang.RuntimePermission)。
"action" 对于许多权限类型而言都是必需的,例如 java.io.FilePermission(指定允许何种类型的文件访问权限)。 对于诸如 java.lang.RuntimePermission 等权限类型则为可选项:既可以在 permission_class_name 之后的 "target_name" 值中指定权限,也可以不指定权限。
权限项的 signedBy 名字/值对为可选项。如果有名字/值对,则表示为已签名权限。意即必须由给定的别名对权限类签名,方可授予权限。例如,假定有以下授权项:
grant { permission Foo "foobar", signedBy "FooSoft"; }如果将 Foo.class 权限放到 JAR 文件中,且该 JAR 文件已由与 "FooSoft" 别名所指定的证书中的公钥相对应的私钥签名,或在 Foo.class 是系统类(因为系统类不受策略限制)的情况下,即可授予 Foo权限类型。
权限项中出现的项目必须按指定顺序出现(permission,permission_class_name,"target_name","action" 和 signedBy "signer_names")。分号表示项终止。
大小写对于标识符(permission、signedBy、codeBase 等)来说并不重要,但对于 permission_class_name 或作为值传递过来的字符串而言就很重要了。
有关 Windows 系统上文件路径规范的注意事项
请注意:在指定 java.io.FilePermission 时,"target_name" 是文件路径。在 Windows 系统上,无论何时在字符串中(而不是在 codeBase URL 中)直接指定文件路径,路径中都需要两个反斜杠来代表一个实际的反斜杠,如下例所示:
grant { permission java.io.FilePermission "C:\\users\\cathy\\foo.bat", "read"; };原因在于:字符串是由符号处理器 (java.io.StreamTokenizer) 来处理的。符号处理器允许将“\”用作转义字符串(例如,“\n”表示换行),因此需要用两个反斜杠来表示一个反斜杠。符号处理器处理完以上文件路径字符串后,将把双反斜杠转换成单个反斜杠,其最终结果为:
"C:\users\cathy\foo.bat"
策略配置文件中两项的示例如下所示:
// 如果代码由 "Duke" 签字,则向 /tmp 中的所有文件 // 授予读/写访问权限: grant signedBy "Duke" { permission java.io.FilePermission "/tmp/*", "read,write"; };// 授予所有用户以下权限:
grant { permission java.util.PropertyPermission "java.vendor"; };
另一个示例策略配置文件如下所示。
grant signedBy "sysadmin", codeBase "file:/home/sysadmin/*" { permission java.security.SecurityPermission "Security.insertProvider.*"; permission java.security.SecurityPermission "Security.removeProvider.*"; permission java.security.SecurityPermission "Security.setProperty.*"; };本示例规定:只有满足以下条件的代码才能调用 Security 类中的方法以添加或删除提供者或者设置 Security 属性:
- 代码将从位于本地文件系统上“/home/sysadmin/”目录下的签名 JAR 文件中加载。
- 可以用密钥仓库中别名“sysadmin”所引用的公钥来校验签名。
可以忽略代码源中两个组件的任何一个(或两者)。下面是忽略 codeBase 的示例:
grant signedBy "sysadmin" { permission java.security.SecurityPermission "Security.insertProvider.*"; permission java.security.SecurityPermission "Security.removeProvider.*"; };如果该策略生效,则来自 JAR 文件(由 "sysadmin" 签名)的代码可以添加/删除提供者,而不管 JAR 文件来源于何处。
下面是没有签名人的示例:
grant codeBase "file:/home/sysadmin/-" { permission java.security.SecurityPermission "Security.insertProvider.*"; permission java.security.SecurityPermission "Security.removeProvider.*"; };这里,来自本地文件系统“/home/sysadmin/”目录下任意位置的代码都可以添加/删除提供者。 该代码不必签名。
下面是既不含 codeBase 也不含 signedBy 的示例:
grant { permission java.security.SecurityPermission "Security.insertProvider.*"; permission java.security.SecurityPermission "Security.removeProvider.*"; };此处,由于两个代码源组件均被忽略,因此任何代码(不管来自于何处,是否已签名或由何人签名)都可添加/删除提供者。
策略文件和安全属性文件中可以进行属性扩展。
属性扩展类似于扩展 shell 中的变量。也就是说,当类似
${some.property}的字符串出现在策略文件或安全属性文件中时,它将被扩展为系统属性的值。 例如,
permission java.io.FilePermission "${user.home}", "read";将把 "${user.home}" 扩展为使用 "user.home" 系统属性的值。如果该属性的值是 "/home/cathy",则以上示例等价于:
permission java.io.FilePermission "/home/cathy", "read";为了能在与平台无关的策略文件中使用,也可采用特殊记号 "${/}"。该记号是 "${file.separator}" 的简化表示。这种方式允许使用下列字符串:
permission java.io.FilePermission "${user.home}${/}*", "read";如果 "user.home" 属性的值是 /home/cathy,而且是在 Solaris 系统上,则以上字符串将转换为:
permission java.io.FilePermission "/home/cathy/*", "read";如果 "user.home" 值是 C:\users\cathy,而且是在 Windows 系统上,则以上字符串将转换为:
permission java.io.FilePermission "C:\users\cathy\*", "read";同样,作为一种特例,如果扩展 codebase 中的属性,例如
grant codeBase "file:${java.home}/lib/ext/"则任何文件分隔符都将自动转换为“/”。这样,在 Windows 系统上,以上字符串将转换为:
grant codeBase "file:C:/jdk1.2/lib/ext/"即使 "java.home" 被设置为 C:\jdk1.2。因此,用户就不必也不应该在 codeBase 字符串中使用 ${/}。
策略文件中允许使用双引号字符串的地方都可进行属性扩展。其中包括 "signer_names"、"URL"、"target_name" 和 "action" 域。
是否允许属性扩展由安全属性文件中的“policy.expandProperties”属性控制。如果该属性为真(缺省值),则允许扩展。
请注意:不能使用嵌套属性;嵌套属性将无效。 例如,
"${user.${foo}}"是无效的,即使将“foo”属性设置为“home”。原因在于属性解析程序不能识别嵌套属性;解析程序只是简单地搜索第一个“${”,然后继续搜索直到找到第一个“}”为止,同时试图将搜索结果(这里是 "${user.$foo}")解释为属性。如果没有这种属性,则解析程序就会发生解释失败。
也请注意:如果在 grant 项、permission 项或 keystore 项中无法扩展某个属性,则该项将被忽略。例如,如果在没有定义系统属性“foo”的情况下使用语句:
grant codeBase "${foo}" { permission ...; permission ...; };则该 grant 项中的所有权限都将被忽略。如果使用语句:
grant { permission Foo "${foo}"; permission Bar; };则将仅忽略“permission Foo...”项。最后,如果使用语句:
keystore "${foo}";则将忽略 keystore 项。
Windows 系统、文件路径和属性的扩展
如上所述,在 Windows 系统上,当直接在字符串中(而不是在 codeBase URL 中)指定文件路径时,用户需要用两个反斜杠来代表文件路径中一个实际的反斜杠,如下例所示:
grant { permission java.io.FilePermission "C:\\users\\cathy\\foo.bat", "read"; };原因在于:字符串是由符号处理器 (java.io.StreamTokenizer) 来处理的。符号处理器允许将“\”用作转义字符串(例如,“\n”表示换行),因此需要用两个反斜杠来表示一个反斜杠。符号处理器处理完以上文件路径字符串后,将把双反斜杠转换成单个反斜杠,其最终结果为:
"C:\users\cathy\foo.bat"符号处理器处理完字符串后,即进行字符串中的属性扩展。因此,如果使用字符串:
"${user.home}\\foo.bat"则符号处理器首先处理字符串,即将双反斜杠转换成单个反斜杠,其结果为:
"${user.home}\foo.bat"随即扩展 ${user.home} 属性,其最终结果为:
"C:\users\cathy\foo.bat"以上假定 "user.home" 的值是 C:\users\cathy。当然,为实现与平台无关,最好在开始指定字符串时不要显式带上斜杠,即可以用 ${/} 属性来代替,如下例所示:
"${user.home}${/}foo.bat"