Java 应用程序环境的策略(对不同来源的代码指定权限)由 Policy 对象来表示。更明确地说,就是由 Policy 类(包含在 java.security 包中)的实现抽象方法的 Policy 子类来表示。

Policy 对象所用策略信息的源位置由 Policy 实现决定。缺省 Policy 实现从静态策略配置文件获得自己的信息。本文档的其余部分叙述了缺省 Policy 实现及其所读取的策略文件中必须使用的语法。有关使用 Policy Tool 来创建策略文件(不必知道所需语法)的详细信息,请参阅《策略工具文档》 (for Solaris) (for Windows)。

以下是本文档其余部分的概要:

缺省 Policy 实现

缺省策略文件位置

更改 Policy 实现

策略文件语法

策略文件示例

策略文件中的属性扩展

相关文档

 

缺省 Policy 实现

在缺省 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 实现类,前提是前者属于抽象 Policy 类的子类并可实现 getPermissions 方法(及其它必要的方法)。

缺省 Policy 实现的更改可通过编辑安全属性文件来完成,其中安全属性文件指 JDK lib/security 目录中的 java.security 文件。

下面给出一种可在 java.security 中设置的属性类型的形式:

    policy.provider=PolicyClassName

PolicyClassName 必须指定所需 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/.keystore

URL 也可以是绝对 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"

发表评论