我在尝试使用 SecurityManager 运行 Java RMI 应用程序时遇到了一个奇怪的错误。当服务器启动时,我希望它从作为命令行参数提供的文件中读取文本。我正在使用 Eclipse,并且该文件与 Java 项目的根目录位于同一目录中(因此我可以在命令行参数中给出文件名而不是完整路径)。我知道 RMI 的 SecurityManager 默认禁止文件 I/O,所以我为我的服务器创建了一个策略文件,如下所示:
grant codeBase "file:///C:/Users/Edward/College/CS197/authmatch/bin/-" {
//Giving the server permission to make connections
permission java.net.SocketPermission "127.0.0.1:1024-", "connect, resolve";
permission java.net.SocketPermission "127.0.0.1:1024-", "accept, resolve";
//File I/O permissions
permission java.io.FilePermission "C:/Users/Edward/College/CS197/authmatch/-", "read,write,delete";
permission java.util.PropertyPermission "user.dir", "read";
permission java.lang.RuntimePermission "readFileDescriptor";
permission java.lang.RuntimePermission "modifyThread";
};
(请注意,我的 Eclipse 项目的名称是“authmatch”,它在 Windows 上运行)。在我的 Eclipse 运行配置中,我使用以下 VM 标志启用此策略文件:
-Djava.rmi.server.codebase=file:///C:/Users/Edward/Documents/College/CS197/authmatch/bin/
-Djava.security.policy=server.policy
我知道正在解析和加载策略文件,因为如果我在 server.policy 中引入语法错误,Java 会在我的应用程序运行时抱怨它(“错误解析文件”)。但是,安全管理器似乎以某种方式忽略了我在策略中授予的权限,因为当我运行应用程序时出现此错误:
Exception in thread "main" java.security.AccessControlException: access denied ("java.io.FilePermission" "smalltest.txt" "read")
at java.security.AccessControlContext.checkPermission(Unknown Source)
at java.security.AccessController.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkRead(Unknown Source)
at java.io.RandomAccessFile.<init>(Unknown Source)
at etremel.authmatch.text.TextFileFormatter.<init>(TextFileFormatter.java:39)
at etremel.authmatch.source.PatternMatcherSource.main(PatternMatcherSource.java:302)
由于我要求它读取的文件(smalltest.txt)位于“authmatch”项目目录中,并且我明确授予我的应用程序读取该目录的权限
permission java.io.FilePermission "C:/Users/Edward/College/CS197/authmatch/-", "read,write,delete";
为什么它仍然坚持认为它没有文件的读取权限?我怀疑这可能是 Windows 问题,因为我在 Linux 计算机上运行了相同的项目,并且类似的策略文件允许它从本地项目目录中读取就可以了。
更新
我运行服务器,-Djava.security.debug=access,failure
它在解析策略文件时生成了一堆调试消息。您可以在这个 pastebin看到整个日志,但似乎有两个重要部分:
access: access allowed ("java.security.SecurityPermission" "getPolicy")
access: access allowed ("java.io.FilePermission" "C:\Users\Edward\Documents\College\CS197\authmatch\bin" "read")
access: domain that failed ProtectionDomain (file:/C:/Users/Edward/Documents/College/CS197/authmatch/bin/ <no signer certificates>)
sun.misc.Launcher$AppClassLoader@74ba86ef
<no principals>
java.security.Permissions@7a8a44a6 (
("java.io.FilePermission" "\C:\Users\Edward\Documents\College\CS197\authmatch\bin\-" "read")
("java.net.SocketPermission" "localhost:1024-" "listen,resolve")
...
...很久以后:
access: access allowed ("java.io.FilePermission" "C:\Users\Edward\Documents\College\CS197\authmatch\bin\etremel\authmatch\text\TextFileFormatter.class" "read")
access: access allowed ("java.util.PropertyPermission" "user.dir" "read")
access: access denied ("java.io.FilePermission" "smalltest.txt" "read")
看起来有某种“域保护”失败,因为我的代码库没有签名,但我认为使用grant codeBase
设置指定没有签名的安全策略是可以接受的。更令人费解的是,它看起来并没有读取根 authmatch 目录的 FilePermission,只是 authmatch/bin 目录。然后它得出结论,它应该拒绝访问“smalltest.txt”,但它永远不会解析该文件的完整目录路径。
请记住,相同的项目和策略在 Linux 上运行良好。