jvm之AccessController.doPrivileged
AccessController.doPrivileged在底层源码中会出现,本文对它进行一个简单介绍及如何使用的说明。
首先解释一下几个相关概念
保护域
类被装入jvm,为每个类指定一个保护域,保护域定义了授予一段特定代码的所有权限,一个保护域对应一个策略policy.txt文件的一个或者多个grant子句,被装入jvm的每一个类型仅属于一个保护域。
那么一个类型如何被指派到保护域呢?
类装载器知道自己装载的所有类和接口的代码库和签名者,它利用这些信息生成CodeSource对象,将CodeSource对象传递给当前policy对象(policy对象代表了一个从代码来源到权限的全局映射,最终还是由类装载器负责决定代码执行时获取什么样权限)的getPermssions方法,得到PermissionCollection抽象类的子类实例,PermissionCollection包含所有Permission对象的引用,利用它创建的CodeSource和它从Policy对象得到的PermissionCollection,可以实例化一个新的保护域PretectDomain对象,然后传递给defineClass方法,来将这段代码放入保护域内。
顺便描述一下以下三个类加载器使用到方法
loadClass:调用findLoadedClass(String) 这个方法,查看这个Class是否已经别加载,如果没有被加载,继续往下走,查看父类加载器,递归调用loadClass(),如果父类加载器是null,说明是启动类加载器,查找对应的Class,如果都没有找到,就调用findClass,一般被重写。
findClass:根据名称或位置加载.class字节码,然后使用defineClass,通常由子类去实现。
defineClass:解析定义.class字节流,返回class对象。
如下Friend和Friend$1是friend.jar的两个class文件
访问控制器
java.security.AccessController提供了一个默认的安全策略执行机制,它使用栈检查来决定潜在不安全的操作是否被允许。
AccessController最核心方法是它的checkPermission静态方法,该方法决定一个特定的操作是否被允许。允许则简单返回,禁止则抛出AccessControlException异常。checkPermission自顶向下检查栈帧,每个栈帧代表了当前线程调用的某个方法,每一个方法是在某个类中定义,每个类又属于某个保护域,每个保护域包含一些权限,因此每个栈帧间接和一些权限相关,要遇到一个没有权限帧就抛出异常。栈检查可以通过使用doPrivileged方法来中断,后续的栈帧对操作的资源不论是否有权限都无关。
implies()方法
表示当前Permission对象 (this) 是否暗含了指定 Permission 对象(permission) 的权限。
permission 子类必须实现此方法,因为它们是惟一能在 permission 对象上施加语义的类。
Java 中给出一个经典实现:BasicPermission,它使用了传入的字符串作为权限的标志,并使用类似于相对路径的办法比较一个 Permission 是否暗含了另一个Permission 的权限。
import java.security.BasicPermission; import java.security.PermissionCollection; public class ImpliesTest { public ImpliesTest() { } public static void testImplies() { MyPermission usaBp = new MyPermission("usa.*"); //全美国 MyPermission chinaBp = new MyPermission("china.*"); //全中国 MyPermission hubeiBp = new MyPermission("china.hubei.*"); //全湖北省 MyPermission wuhanBp = new MyPermission("china.hubei.wuhan.*"); //全武汉市 MyPermission wuchangBp = new MyPermission("china.hubei.wuhan.wuchang.*"); //全武昌区 System.out.println(chinaBp.implies(usaBp)); //false全美国并不暗含全中国 System.out.println(hubeiBp.implies(wuchangBp)); //true全湖北暗含了全武昌 System.out.println(hubeiBp.implies(chinaBp)); //false全湖北并不暗含全中国 // Java 对于权限还给出一个权限集合类PermissionCollection,它是一组权限的并集。 // 对任意给定的Permission进行测试权限,只要被这个集合中的任意一个Permission 暗含即可。 // 需要注意的是,该集合中只能是同种类型的Permission。 PermissionCollection bpc = usaBp.newPermissionCollection(); bpc.add(chinaBp); System.out.println(bpc.implies(hubeiBp)); //true(全美国 | 全中国)暗含了全湖北 } public static void main(String[] args) { testImplies(); } } class MyPermission extends BasicPermission { private static final long serialVersionUID = 1L; public MyPermission(String name) { super(name); } } // 通过文件目录读权限测试implies方法 Permission file = new FilePermission("/tmp/f", "read"); Permission star= new FilePermission("/tmp/*", "read"); boolean sif = star.implies(file) // 输出true boolean fis= file.implies(star) // 输出false
栈检查机制
假设有这样一种情况:程序A想在/tmp目录中新建一个文件,它没有相应的权限,但是它引用了另外一个B.Jar包,刚好B有权限在/tmp目录中新建文件,而B在新建文件的时候采用的是AccessController.doPrivileged方法进行的,这种情况下A就可以调用B的创建文件方法进行文件创建。
AccessController.doPrivileged中断了栈检查过程,使得后续原本没有权限的代码也可以正常执行,从而成功创建文件,如果不使用AccessController.doPrivileged,会一直进行栈检查直到栈底位置,在程序A的栈帧(栈底)中会抛出权限异常,文件创建失败。
示例参考:http://www.blogjava.net/Phrancol/articles/259069.html
总结:
在某一个线程的调用栈中,当 AccessController的checkPermission方法被最近的调用程序调用时,对于程序要求的所有访问权限,ACC决定是否授权的基本算法如下:
1. 如果调用链中的某个调用程序没有所需的权限,将抛出AccessControlException。
2. 若是满足以下情况即被授予权限:
a.调用程序访问另一个有该权限域里程序的方法,并且此方法标记为有访问特权。
b.调用程序所调用(直接或间接)的后续对象都有上述权限。
本文转载,原文链接:
jvm之AccessController.doPrivileged_大佬请赐教-CSDN博客 https://blog.csdn.net/jiangtianjiao/article/details/87909065
相关链接:AccessController.doPrivileged
【转】关于AccessController.doPrivileged - 简书 https://www.jianshu.com/p/3fe79e24f8a1
Java 安全模型介绍 – IBM Developer https://developer.ibm.com/zh/articles/j-lo-javasecurity/
对AccessController.doPrivileged一点了解 | 学步园 https://www.xuebuyuan.com/659682.html