浅谈Java访问控制机制

  Java 访问控制机制的原理是:在某些策略配置文件中预定义好某些代码对某些资源具有某些操作权限,当某些代码以某个权限访问某个资源的时候,如果对该资源预指定的权限中没有该权限,则禁止访问,否则可以访问。

  上面一段话读起来比较晦涩,下面先以数据库用户和数据表为例来说明。

  指定某些代码对某些资源具有某些操作权限

  某些代码:用户 Admin, X

  某些资源:数据库表 User

  操作权限:用户 Admin(某些代码)对 User 表(某些资源) 具有 CRUD 权限

  用户 X(某些代码)对 User 表(某些资源) 具有 R 权限

  某些代码以某个权限访问某个资源

  用户 X(某些代码)查询(某个权限) User 表(某些资源),因为预定义中用户 X 对 User 表是有查询权限的,所以检查通过

  用户 X(某些代码)删除(某个权限) User 表(某些资源) 中某条数据,因为预定义中用户 X 对 User 表只有查询权限,没有删除权限的,所以检查不通过

  在 Java 中,道理也是一样的,只是 Java 中某个类相当于上面的某个用户,如 ClassA 相当于用户 Admin, ClassB 相当于用户 X。 但是在 Java 中情况稍微复杂一点,比如 ClassB 可以调用 ClassA 的方法,那么这时候,权限将如何检查呢?还是以例子来说明。

  指定某些代码对某些资源具有某些操作权限

  某些代码:用户 ClassA, ClassB

  某些资源:某个配置文件 x.properties

  操作权限:ClassA(某些代码)对 x.properties(某些资源) 具有读写权限

  ClassB(某些代码)对 x.properties(某些资源) 具有读权限

  某些代码以某个权限访问某个资源

  ClassA(某些代码)读或写(某个权限) 配置文件x.properties(某些资源),因为预定义中ClassA 对 x.properties是有读和写权限的,所以检查通过

  ClassB(某些代码)读(某个权限) 配置文件x.properties(某些资源),因为预定义中ClassB对 x.properties是有读权限的,所以检查通过

  ClassB(某些代码)写(某个权限) 配置文件x.properties(某些资源),因为预定义中ClassB对 x.properties是没有写权限的,所以检查不通过

  Class B 调用 ClassA 的写 x.properties 文件方法,是什么检查过程呢?

  在 Java 中,不管有几个类的反复调用,最终都是以某个权限操作某个资源,如这里是 Class B 调用 ClassA,其最终操作权限是以写的权限去操作 x.properties 文件。那么 Java 就会对反复调用过程中的所有类进行检查,看它们对 x.properties 文件是否有写权限,如果都有则通过,只要任何一个类没有该权限,则不通过。

  其中检查的过程与调用过程是互为逆向的。如在上例中,ClassB 调用 ClassA 的去写 x.properties 文件,那么 JVM 就会先检查 ClassA 对 x.properties 文件是否有写权限,因为预定义中有,所以 ClassA 通过。接下来检查 ClassB,因为预定义中,ClassB对 x.properties 文件没有写 权限,所以检查不通过,本次操作失败。

  在 Java 访问控制机制中,主要有以下几个类:

  SecurityManager  AccessController  AccessControlContext  ProtectionDomain  PermissionCollection  Permission  Policy  SecurityManager/ AccessController

  访问控制的入口类。在 Java 中,访问控制默认是关闭的。

  AccessControlContext

  类的调用过程上下文。它以栈的形式保存类的调用过程,如在上面例子中,ClassB 调用 ClassA,那么 AccessControlContext 的栈底是 ClassB,栈顶是 ClassA,如果 ClassA 再调用 ClassC,那么 ClassC 在压入栈,栈顶变成了 ClassC

  ProtectionDomain

  这是一个关键的类。它定义了“某些代码对某些资源具有某些操作权限”,亦即那个目录下的代码对那些资源有哪些操作权限。它有三个属性:

  某些代码

  一般是指某个目录下的所有代码,如 “JRE_HOEM/lib/ext”在 Java 中表示为 CodeBase

  某些资源

  比如 conf 目录下的文件,在 Java 中表现为某个文件(夹)路径 BasePath

  某些权限

  是指某些代码对对某些资源操作权限的集合,比如读权限、写权限,在 Java 中表现为 PermissionCollection 类。

  PermissionCollection

  权限的集合类。如文件读权限、文件写权限的集合:

  FilePermission(file,SecurityConstants.FILE_WRITE_ACTION)  FilePermission(file,SecurityConstants.FILE_READ_ACTION)

  Permission

  某个具体的权限。如文件读权限 FilePermission(file,SecurityConstants.FILE_WRITE_ACTION)

  Policy

  “指定某些代码对某些资源具有某些操作权限”的策略定义在策略文件中,Policy 负责从配置文件中读取这些策略,并根据策略文件构建 ProtectionDomain。JVM 中只有一个 Policy 对象。

  “某些代码对某些资源具有某些操作权限”建立过程

  在 Java 中,每个类都有一个 ProtectionDomain 的一个引用,因为每个类都属于某个特定的目录下,这样就可以知道某个类对某些资源有哪些操作权限了。下面看每个类与 ProtectionDomain 建立关系的过程:

  当 ClassLoader 去加载一个 class 的时候,它会把这个类及授给这个类的权限集封装到“java.security.ProtectionDomain”中,其中这些权限是通过 ClassLoader并根据策略文件进行分配的。

  1) 找到类字节码的加载 URL

  2) 如果这个类是从包 java.* 中加载的,那就把该类与内在的 System ProtectionDomain 关联,其中 System ProtectionDomain 是具有全部权限(AllPermission)的。如果该类不是从包 java.* 中加载的,则继续

  3) 如果该类要关联的 ProtectionDomain 以及存在,则直接添加它们之间引用关系即可,否则继续

  4) 创建一个 CodeSource 对象,里面封装了类字节码的加载路径

  5) 创建一个 PermissionCollection 对象,并把策略文件中赋给 CodeSource 的权限封装到该 PC 对象中

  6) 创建一个 ProtectionDomain,里面封装了 CodeSource 和 PermissionCollection 对象,即那些代码对那些资源有那些操作权限:ProtectionDomain = new ProtectionDomain(CodeSource,PermissionCollection)

  7) 把刚加载的 class 类与当前的 ProtectionDomain 关联

  各个类之间引用调用关系这里不阐述,下面继续看例子:

  现在假设有如下代码:${user.dir}/testA/ClassA ${user.dir}/testB/ClassB

  有如下资源:${user.dir}/conf/x.properties

  加入在 Java 策略文件中添加如下策略:

  grant codeBase “file:${user.dir}/testA/*” {permission java.io.FilePermission “${user.dir}/conf *”, “read”;permission java.io.FilePermission “${user.dir}/conf *”, “write”;};grant codeBase “file:${user.dir}/testB/*” {permission java.io.FilePermission “${user.dir}/conf *”, “read”;};

  上面有提到,在Java 中每个 Class都保存有 ProtectionDomain的引用,这里 ClassA 和 ClassB 的 ProtectionDomain 模型如下:

  

  

  下面假设 ClassB 调用 ClassA 的去写 x.poperties 文件,那么 AccessControlContext 和 PermissionCollection 对应模型是这样的:

  

  当 AccessController.checkPermission 开始检查权限时,它知道要本次要检查的权限是 FilePermission(“x.properties”, “write”),即对 “x.properties”的写权限。于是它先获取调用过程上下文中所有类(main(JVM)–>ClassB–>ClassA–>FileOutputStream–>AccessController)的 ProtectedDomain 找出来:

  JVM 从 mian 执行的,它的 ProtectedDomain 是 SystemDomain

  ClassA 的 ProtectedDomain 是 ProtectedDomainA

  ClassB 的 ProtectedDomain 是 ProtectedDomainB

  FileInputStream 和 AccessController 的 ProtectedDomain 是 SystemDomain

  把访问上下文的 ProtectedDomain 都找出来后,就逐个检查每个 ProtectedDomain 是否有 FilePermission(“x.properties”, “write”) 权限。因为在 Java 访问控制机制中,它要确保每个调用的类都有该权限才能通过检查。

  于是,开始做下面检查:

  1) AccessController–>SystemDomain : Java 内部代码保护域,具有所有操作权限,检查通过

  2) FileInputStream–>SystemDomain : Java 内部代码保护域,具有所有操作权限,检查通过

  3) ClassA–>ProtectedDomainA: 在上面的 ProtectedDomainA 模型中,我们知道它是有 FilePermission(“x.properties”, “write”) 权限的,检查通过

  4) ClassB–>ProtectedDomainB: 在上面的 ProtectedDomainB 模型中,我们知道它没有 FilePermission(“x.properties”, “write”) 权限的,检查不通过

偶尔被惊鸿一瞥的美丽吸引;或者走进一条深沉深沉的巷道,

浅谈Java访问控制机制

相关文章:

你感兴趣的文章:

标签云: