---- 眾所周知,Java語言具有完善的安全框架,從編程語言,編譯器、解釋程序到Java虛擬機,都能確保Java系統不被無效的代碼或敵對的編譯器暗中破壞,基本上,它們保證了Java代碼按預定的規則運作。但是,當我們需要逾越這些限制時,例如,讀寫文件,監聽和讀寫Socket,退出Java系統等,就必須使用數字簽名或安全策略文件(*.Policy)。

---- 在企業內部網中,本文提出了使用安全策略文件來設置java程序權限的一種簡單的方法。由于企業內部網中各臺計算機的位置、用途和安全性明確,更適于使用安全策略文件來設置java的權限,軟件的安裝、設置、升級和遷移都非常的方便,并且,還可以和數字簽名配合使用,更重要的是,可以細分每個java程序的權限,使用起來靈活方便。

一. Java中安全策略的概念
---- Java應用程序環境的安全策略,詳細說明了對于不同的代碼所擁有的不同資源的許可,它由一個Policy對象來表達。為了讓applet(或者運行在SecurityManager下的一個應用程序)能夠執行受保護的行為,例如讀寫文件,applet(或Java應用程序)必須獲得那項操作的許可,安全策略文件就是用來實現這些許可。
---- Policy對象可能有多個實體,雖然任何時候只能有一個起作用。當前安裝的Policy對象,在程序中可以通過調用getPolicy方法得到,也可以通過調用setPolicy方法改變。Policy對象評估整個策略,返回一個適當的Permissions對象,詳細說明那些代碼可以訪問那些資源。

---- 策略文件可以儲存在無格式的ASCII文件,或Policy類的二進制文件,或數據庫中。本文僅討論無格式的ASCII文件的形式。

二. Policy文件的格式
---- 為了能夠更好地理解下面的內容,建議在閱讀時參照\jdk1.2\jre\lib\security\java.policy文件和\jdk1.2\jre\lib\security\java.security文件的內容。
---- 1. Policy文件的語法格式與說明

---- 一個Policy文件實質上是一個記錄列表,它可能含有一個“keystore”記錄,以及含有零個或多個“grant”記錄。其格式如下:

keystore "some_keystore_url",
"keystore_type";

grant [SignedBy "signer_names"]
[, CodeBase "URL"] {
Permission permission_class_name
[ "target_name" ]
[, "action"] [, SignedBy "signer_names"];
Permission ...
};

---- 1.1"keystore"記錄
---- 一個keystore是一個私有密鑰(private keys)數據庫和相應的數字簽名,例如X.509證書。Policy文件中可能只有一條keystore記錄(也可能不含有該記錄),它可以出現在文件中grant記錄以外的任何地方。Policy配置文件中指定的keystores用于尋找grant記錄中指定的、簽名者的公共密鑰(public keys),如果任何grant記錄指定簽名者(signer_names),那么,keystore記錄必須出現在policy配置文件中。

---- "some_keystore_url"是指keystore的URL位置,"keystore_type"是指keystore的類型。第二個選項是可選項,如果沒有指定,該類型則假定由安全屬性文件(java.security)中的"keystore.type"屬性來確定。keystore類型定義了keystore信息的存儲和數據格式,用于保護keystore中的私有密鑰和keystore完整性的算法。Sun Microsystems支持的缺省類型為“JKS”。

---- 1.2"grant"記錄

---- 在Policy文件中的每一個grant記錄含有一個CodeSource(一個指定的代碼)及其permission(許可)。

---- Policy文件中的每一條grant記錄遵循下面的格式,以保留字“grant”開頭,表示一條新的記錄的開始,“Permission”是另一個保留字,在記錄中用來標記一個新的許可的開始。每一個grant記錄授予一個指定的代碼(CodeBase)一套許可(Permissions)。

---- permission_class_name必須是一個合格并存在的類名,例如java.io.FilePermission,不能使用縮寫(例如,FilePermission)。

---- target_name用來指定目標類的位置,action用于指定目標類擁有的權限。

---- target_name可以直接指定類名(可以是絕對或相對路徑),目錄名,也可以是下面的通配符:

directory/* 目錄下的所有文件
*當前目錄的所有文件
directory/-目錄下的所有文件,包括子目錄
- 當前目錄下的所有文件,包括子目錄
《ALL FILES》文件系統中的所有文件
對于java.io.FilePermission,action可以是:
read, write, delete和execute。
對于java.net.SocketPermission,action可以是:
listen,accept,connect,read,write。

---- 1.3 Policy文件中的屬性擴展(Property Expansion)
---- 屬性擴展與shell中使用的變量擴展類似,它的格式為:

"${some.property}"
實際使用的例子為:
permission java.io.FilePermission
"${user.home}", "read";
"${user.home}"的值為"d:\Project",
因此,下面的語句和上面的語句是一樣的:
permission java.io.FilePermission "
d:\Project ", "read";

三. 實例
---- 當初始化Policy時,首先裝載系統Policy,然后再增加用戶Policy,如果兩者都不存在,則使用缺省的Policy,即原始的沙箱模型。
---- 系統Policy文件的缺省位置為:

{java.home}/lib/security/java.policy (Solaris)
{java.home}\lib\security\java.policy (Windows)
用戶Policy文件的缺省位置為:
{user.home}/.java.policy (Solaris)
{user.home}\.java.policy (Windows)

---- 其實,在實際使用中,我們可能不會象上面介紹的那么復雜,特別是在不使用數字簽名時。這時,我們完全可以借鑒JDK 1.2提供給我們的現成的\jdk1.2\jre\lib\security\java.policy文件,根據我們的需要作相應的修改,本文就針對不使用數字簽名情況詳細說明安全策略文件的用法。
---- 下面,是一個完整的在Windows 95/98/NT下使用的.java.policy文件。在文件中,分別使用注釋的形式說明了每個“permission”記錄的用途。

// For LanServerTalk.java and LanClientTalk.java

grant {
//對系統和用戶目錄“讀”的權限
permission java.util.PropertyPermission
"user.dir", "read";
permission java.util.PropertyPermission
"user.home", "read";
permission java.util.PropertyPermission
"java.home", "read";
permission java.util.PropertyPermission
"java.class.path", "read";
permission java.util.PropertyPermission
"user.name", "read";

//對線程和線程組的操作權限
permission java.lang.RuntimePermission
"modifyThread";
permission java.lang.RuntimePermission
"modifyThreadGroup";

//操作Socket端口的各種權限
permission java.net.SocketPermission
"-", "listen";
permission java.net.SocketPermission
"-", "accept";
permission java.net.SocketPermission
"-", "connect";
permission java.net.SocketPermission "-", "read";
permission java.net.SocketPermission "-", "write";

//讀寫文件的權限
permission java.io.FilePermission "-", "read";
permission java.io.FilePermission "-", "write";

//退出系統的權限,例如System.exit(0)
permission java.lang.RuntimePermission "exitVM";
};

四. java.policy文件的使用
---- 對于windows 95/98/NT,使用.java.policy文件的方法主要有下面兩種。
---- 1. 使用缺省目錄

---- 我們可以簡單地將編輯好的.java.policy文件拷貝到windows 95/98/NT的HOME目錄,這時,所有的applet(或Java應用程序)可能都擁有某些相同的權限,使用起來簡單,但不靈活(例如:對于java.io.FilePermission ,其目標類的target_name必須使用絕對路徑),如果不是在企業內部網中使用,還可能存在一定安全隱患。

---- 2. 在命令行中指定

---- 在命令行,如果我們希望傳遞一個Policy文件給appletviewer,還可以使用"-J-Djava.security.policy"參數來指定policy的位置:

appletviewer -J-Djava.security.
policy=pURL myApplet

---- pURL為Policy文件的位置。下面,是一個實際的例子,以當前目錄的.java.policy文件所指定的安全策略運行當前目錄的LanServerTalk.html(文件中裝載并運行LanServerTalk.java):
appletviewer -J-Djava.security.policy
=.java.policy LanServerTalk.html

---- 這種方法使用靈活,特別是作為一個軟件包在企業內部網中發布時,安裝、設置和遷移軟件,基本無須修改Policy文件的內容,使用起來相當簡單,而且,安全許可的范圍控制較精細。

__________________________________________________________________________________

缺省策略實現和策略文件語法

上次修改時間: 1998 年 10 月 30 日

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”策略文件值(對于 javaappletviewer 命令)。缺省值為“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),而且還包括對包含與簽寫代碼的私鑰相對應的公鑰的證書之引用。代碼源中的證書通過用戶密鑰倉庫中的符號別名引用。

每個授權項包括一個或多個“權限項”,前面為可選 codeBasesignedBy 名字/值對,用于指定要授予權限的代碼。授權項的基本格式如下所示:

  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 域

signedBycodeBase 名字/值對為可選域,其間的順序無關緊要。

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.jarjava.sun.com/people/gong/

java.sun.com/people/gong/appl.jarjava.sun.com/people/gong/-

java.sun.com/people/gong/appl.jarjava.sun.com/people/gong/*

java.sun.com/people/gong/appl.jarjava.sun.com/people/-

java.sun.com/people/gong/appl.jarjava.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.FilePermissionjava.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 權限類型。

權限項中出現的項目必須按指定順序出現(permissionpermission_class_name,"target_name","action" 和 signedBy "signer_names")。分號表示項終止。

大小寫對于標識符(permissionsignedBycodeBase 等)來說并不重要,但對于 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"