一、概述
总的来说,Java安全应该包括两方面的内容,一是Java平台(即是Java运行环境)的;二是Java语言开发的应用程序的安全性。由于我们不是Java本身语言的制定开发者,所以第一个安全性不需要我们考虑。其中第二个安全性是我们重点考虑的问题,一般我们可以通过安全管理器机制来完善安全性。当运行未知的Java程序的时候,该程序可能有恶意代码(删除系统文件、重启系统等),为了防止运行恶意代码对系统产生影响,需要对运行的代码的权限进行控制,这时候就要启用Java安全管理器。
二、SecurityManager出现的场景
比如在阅读源码的时候,发现这样的代码,想了解是做什么的:
SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkWrite(name); }
亦或者在本机运行正常,在服务器运行报错,想解决问题:
Exception in thread "main" java.security.AccessControlException: access denied (java.lang.RuntimePermission createSecurityManager) at java.security.AccessControlContext.checkPermission(AccessControlContext.java:374) at java.security.AccessController.checkPermission(AccessController.java:549) at java.lang.SecurityManager.checkPermission(SecurityManager.java:532) at java.lang.SecurityManager.(SecurityManager.java:282) at xia.study._01Thread.ThreadTest.creatThread1(ThreadTest.java:18) at xia.study._01Thread.ThreadTest.main(ThreadTest.java:13)
这时候具备一些SecurityManager的基础知识还是有必要的。
三、管理器配置文件

3.1 默认配置文件

默认的安全管理器配置文件是 $JAVA_HOME/jre/lib/security/java.policy,即当未指定配置文件时,将会使用该配置。内容如下:
// Standard extensions get all permissions by default grant codeBase "file:${{java.ext.dirs}}/*" { permission java.security.AllPermission; }; // default permissions granted to all domains grant { // Allows any thread to stop itself using the java.lang.Thread.stop() // method that takes no argument. // Note that this permission is granted by default only to remain // backwards compatible. // It is strongly recommended that you either remove this permission // from this policy file or further restrict it to code sources // that you specify, because Thread.stop() is potentially unsafe. // See the API specification of java.lang.Thread.stop() for more // information. permission java.lang.RuntimePermission "stopThread"; // allows anyone to listen on un-privileged ports permission java.net.SocketPermission "localhost:1024-", "listen"; // "standard" properies that can be read by anyone permission java.util.PropertyPermission "java.version", "read"; permission java.util.PropertyPermission "java.vendor", "read"; permission java.util.PropertyPermission "java.vendor.url", "read"; permission java.util.PropertyPermission "java.class.version", "read"; permission java.util.PropertyPermission "os.name", "read"; permission java.util.PropertyPermission "os.version", "read"; permission java.util.PropertyPermission "os.arch", "read"; permission java.util.PropertyPermission "file.separator", "read"; permission java.util.PropertyPermission "path.separator", "read"; permission java.util.PropertyPermission "line.separator", "read"; permission java.util.PropertyPermission "java.specification.version", "read"; permission java.util.PropertyPermission "java.specification.vendor", "read"; permission java.util.PropertyPermission "java.specification.name", "read"; permission java.util.PropertyPermission "java.vm.specification.version", "read"; permission java.util.PropertyPermission "java.vm.specification.vendor", "read"; permission java.util.PropertyPermission "java.vm.specification.name", "read"; permission java.util.PropertyPermission "java.vm.version", "read"; permission java.util.PropertyPermission "java.vm.vendor", "read"; permission java.util.PropertyPermission "java.vm.name", "read"; };
四、启动安全管理器
一般而言,Java程序启动时并不会自动启动安全管理器,可以通过以下两种方法启动安全管理器:建议使用启动参数方式。

4.1 启动参数方式

启动程序的时候通过附加参数启动安全管理器:-Djava.security.manager
若要同时指定配置文件的位置那么示例如下:-Djava.security.manager -Djava.security.policy="E:/java.policy"

4.2 编码方式启动

也可以通过编码方式启动,不过不建议:
System.setSecurityManager(new SecurityManager());
通过参数启动,本质上也是通过编码启动,不过参数启动使用灵活,项目启动源码如下(sun.misc.Launcher):
// Finally, install a security manager if requested String s = System.getProperty("java.security.manager"); if (s != null) { SecurityManager sm = null; if ("".equals(s) || "default".equals(s)) { sm = new java.lang.SecurityManager(); } else { try { sm = (SecurityManager)loader.loadClass(s).newInstance(); } catch (IllegalAccessException e) { } catch (InstantiationException e) { } catch (ClassNotFoundException e) { } catch (ClassCastException e) { } } if (sm != null) { System.setSecurityManager(sm); } else { throw new InternalError( "Could not create SecurityManager: " + s); } }
可以发现将会创建一个默认的SecurityManager;
五、配置文件简单解释

5.1 配置基本原则

在启用安全管理器的时候,配置遵循以下基本原则:
没有配置的权限表示没有。只能配置有什么权限,不能配置禁止做什么。同一种权限可多次配置,取并集。统一资源的多种权限可用逗号分割。

5.2 默认配置文件解释

第一部分授权:
grant codeBase "file:${{java.ext.dirs}}/*" { permission java.security.AllPermission; };
授权基于路径在"file:${{java.ext.dirs}}/*"的class和jar包,所有权限。
第二部分授权:
grant { permission java.lang.RuntimePermission "stopThread"; …… }
这是细粒度的授权,对某些资源的操作进行授权。具体不再解释,可以查看javadoc。如RuntimePermission的可授权操作经查看javadoc如下:

权限目标名称权限所允许的操作允许此权限所带来的风险
createClassLoader创建类加载器授予该权限极其危险。能够实例化自己的类加载器的恶意应用程序可能会在系统中装载自己的恶意类。这些新加载的类可能被类加载器置于任意保护域中,从而自动将该域的权限授予这些类。
getClassLoader类加载器的获取(即调用类的类加载器)这将授予攻击者得到具体类的加载器的权限。这很危险,由于攻击者能够访问类的类加载器,所以攻击者能够加载其他可用于该类加载器的类。通常攻击者不具备这些类的访问权限。
setContextClassLoader线程使用的上下文类加载器的设置在需要查找可能不存在于系统类加载器中的资源时,系统代码和扩展部分会使用上下文类加载器。授予 setContextClassLoader 权限将允许代码改变特定线程(包括系统线程)使用的上下文类加载器。
enableContextClassLoaderOverride线程上下文类加载器方法的子类实现在需要查找可能不存在于系统类加载器中的资源时,系统代码和扩展部分会使用上下文类加载器。授予 enableContextClassLoaderOverride 权限将允许线程的子类重写某些方法,这些方法用于得到或设置特定线程的上下文类加载器。