Java技术提供了三种确保安全的机制:
类加载器可以在将类加载到虚拟机的时候检查类的完整性。
类加载器主要有三种:引导类加载器、平台类加载器、系统类加载器
除引导类加载器外,其他的类加载器都有其父类加载器,层次结构如下图。根据规定,类加载器会为其父类加载器提供一个机会,以便加载任何给定的类。也就是说,当通过系统类加载器加载一个类 StringBuilder 时,它首先要求平台类加载器进行加载,而平台类加载器要求引导类加载器进行加载,如引导类加载器找到了这个类,那么它会加载这个类,而无需其他类加载器进行更多的搜索。
注:引导类加载器没有对应的 ClassLoader 对象
注:java9 之前可以通过反射机制访问受保护的URLClassLoader.addURLs 方法来加载 jar 包,但是现在无法这样操作了
自定义类加载器继承 ClassLoader 类,实现其 findClass 方法即可。
protected Class<?> findClass(String name) throws ClassNotFoundException { try { // loadClassBytes 方法需自己实现 byte[] classBytes = loadClassBytes(name); // defineClass 是 ClassLoader 内部方法 Class<?> cl = defineClass(name,classBytes, 0, classBytes.length); if (cl == null) throw new ClassNotFoundException(name); return cl; } catch (IOExcetion e) { throw new ClassNotFoundException(name); } }
类加载器将字节码序列加载到虚拟机时,这些字节码首先要接受校验器的校验。校验器负责检查是否存在明显的有破坏性的指令。
检查项包括:
java -noverify xxx
注:Java 程序默认不开启安全管理器
有两种方式可以开启安全管理器:
java -Djava.security.manager -Djava.security.policy==xxx.policy xxx
System.setSecurityManager(new SecurityManager());
通过启动参数开启或者在主程序中设置时都可以选择指定策略文件,如果不指定,则使用默认的策略文件(位于 JDK 目录下的 conf/security 文件夹地下)
策略文件也可以通过代码指定(需在开启安全管理器之前指定),如下:
System.setProperty("java.security.policy", "/xxx/xxx.policy"); System.setSecurityManager(new SecurityManager());
一般来说,最好通过启动参数的方式来开启安全管理器和设置策略文件。
基本格式
// 代码来源是可选项,如果为空,则表示对所有来源的代码都授予下列权限 grant <代码来源> { permission 权限类型 "权限名称", "操作列表"; permission 权限类型 "权限名称", "操作列表"; };
代码来源格式为:codeBase "file:///xxx/xxx.jar"
例子:
grant codeBase "http://www.horstmann.com/classes" { // 允许对/tmp目录及其子目录进行读写 permission java.io.FilePermission "/tmp/-", "read,write"; // 运行连接域名为 *.horstmann.com 和端口范围为 8000 到 9999 之间的 socket 服务 permission java.net.SocketPermission "*.horstmann.com:8000-9999", "connect"; };
权限类型可参阅 java.security.permission
类的实现类。
注:如果使用了 Java 平台模块,那么需要在 module-info.java 文件中导出自定义权限类所在的包,否则不生效
除了 JDK 提供的权限类之外,我们也可以自定义权限类。
// TV 权限类 public class TVPermission extends Permission { ... ... // 当前权限是否隐含给定权限 public boolean implies(Permission other) {} }
如果使用了模块,那么必须导出,这样安全模块才可以访问到该权限类。
module-info.java 文件
module xxx { exports com.fy.xxx.permissions; }
grant { permission com.fy.xxx.permissions.TVPermission "tv", "watch"; }
var manage = System.getSecurityManager(); var p = new TVPermission("tv","watch"); // 如果当前代码没有该权限,则这里会抛出 SecurityException 异常 if(manage != null) manage.checkPermission(p);