java security浅谈

java security浅谈

1.介绍java security
在介绍java security之前,我们首先明确为什么会出现java security。可能我们在写代码的时候都没有觉察到有任何的安全机制在。然而,如果我们仔细的思考一下,java security其实无处不在,比如,你访问某一个文件的时候,这里面其实就有安全策略(即这个目录是否可以由当前代码访问),再比如,我想获得某个类的加载器,然后用这个类的加载器去加载类,这里就会有问题 ------当前代码是否被允许获得类加载器,从而加载这个类加载器加载的类。


2.沙箱模型
sandbox(沙箱或沙盘)--即一个虚拟的系统程序,你可以在这个沙盘环境中运行浏览器,或者其它程序,这些程序对环境的变化可以随后删除。并且不会对你的电脑硬盘产生任何结果。举个通俗的例子,你可以有一个沙箱,然后在里面进行原子弹试验,实验结束后就把这个沙箱扔掉,这样的好处就是,原子弹的辐射根本不会对沙箱外部的人有任何的伤害 

-----沙箱模型       
  




通过上面我们可以看到远程代码(remote code)被放到沙箱中运行,它的任何行为不会影响到jvm中其它代码的运行,当然它也就只能访问沙箱提供的有限资源。本地代码(local code)是被信任的


java security的整体安全性通过多种方式实现

  1. 从java语言的层面上说,java是类型安全(private)且易于使用的,内存自动管理,垃圾回收,字符串和数组的范围检查功能
    解释:类型安全是指,一个类不可能访问另一个类的私有属性和私有方法;内存自动管理,和垃圾回收我们遇见过,最常见的就是,当一个对象没有了指向这个对象的引用的时候,就会被垃圾回收器回收;至于字符串和数组的范围检查功能,jvm在运行时会抛出数组越界异常
  2.编译器和字节码验证器确保只执行合法的java字节码。字节码验证器与java虚拟机一起在运行时保证语言安全
解释:字节码验证器确保这个是合法的java编译后的代码,而不是某个黑客纯手写的字节码文件

        3. 类加载器定义了本地命名空间,可用于确保不受信任的小程序不会干扰其他程序的运行
解释:我们知道类加载器的作用是按照一定的规则从某个位置加载类,注意了:抛出一个定义!!! 因为许多类可以被同一个类加载器加载, 故我们定义,由同一个类加载器加载的全部的类构成了一个命名空间



如果按照上述的说法我们可以重新去理解类加载器的双亲委派模型


原始的双亲委派模型

接下来我们用类加载器命名空间的概念去解释类加载的机制(也就是双亲委派模型)


从上图我们可以看出,每一个ClassLoader代表了一个作用域,作用于里面的内容可以表示为它们能够加载的类,
当AppClassLoader想要加载某个类的时候 ,他就首先会去请求父类加载器加载类,如果父类加载器不能够提供类的话,
那么类加载器就在自己的命名空间中查找类(这和编程语言中的作用域还不一样,编程语言中查找 变量的时候首先在
前作用域查找,如果没有查找到,那么去父作用域查找)。。。。插入一段笑话,我们知道,AppClassLoader的父加载
器是ExtClassLoader,ExtClassLo ader的父加载器是BootstrapClassLoader
,但是,注意了,他们并没有语言上的继承关系,只有语义上的关系,仔细想一下,这是一种什么关系。。。。。其实
就是干爹的关系。哈哈哈




3.演练沙箱模型
java2的演练沙箱模型相对于java1.0的沙箱模型,区别在于,它把本地代码不是设置为内置(built-in)安全的。本地代码或
者远程代码通过不同的类加载器加载进来的时候,会被放在不同的类加载器的命名空间中(这个概念我们上面提到过),远程
代码还是会被放到沙箱中----------(注意:在这里我没有写java1.2的沙箱模型,java1.2在java1.0的基础上,为远程代码添加了
数字签名,如果jvm核对了数字签名,那么远程代码是被信任的(trusted),它可以访问部分的系统资源。



4.新的保护机制 ------保护域的概念
一个域可以由当前主体直接访问的一组对象的范围而定( 解释:你能力所触及的范围,就是一个域---有多大能耐(直接访问的一组对象的范围),就有相应的多大的天空(域))。其中委托人是计算机系统中授予其权限的实体(解释: 这句话表示域的实体是什么-----即拥有权限的实体)---------你可能还是不理解,没事我们接着往下说
       
        保护域概念作为保护单位之间的分组和隔离的便利机制(因为每一个保护域代表这不同的权限,要想获得相应的权限,只需要将类放入相应的保护域即可)。可以将保护域彼此交互分开,以便任何允许的交互必须通过可信系统代码或有关域明确允许(解释:在某一个域中的类,想要获得另一个域中的权限,需要通过可信的系统代码,或者有关域的允许)。

保护域通常分为两个不同的类别: 系统域和应用程序域。我们说域是拥有权限的实体,那么自然而然系统域拥有者受保护的外界资源的权利,换句话说,如果你的代码,想要访问IO,或者文件,是要通过系统域的。应用程序域代表你的应用程序当前所拥有的权限。我们还在下图还看到,应用程序域和系统域有重叠的地方,这个其实就是我们上面所说的两个域之间的交互,应用程序域的代码想要访问系统域,即获得系统域的权限,举个例子来说,在你的应用程序的代码中,你想在D盘下创建一个文件,这个过程其实就是应用程序域想要获取系统域的权限。(见图2)

图1



图2

域上面包含一组类,其实例被授予相同的权限集。保护域由当前有效的政策决定。



· 可以看到不同的类被加载到了不同的保护域,domain就是我们所说的保护域,而在java语言中,保护域用类ProtectionDomain来表示。这里有一个问题:上图的模型和我们所说的由同一个类加载器加载的类属于同一个类加载器的命名空间之间有什么联系呢? 见下图







--------------------------------------------------------第二部分,java语言是怎么体现上面的原理的---------------------我是萌萌的分界线->_<----------前面那个傻逼分界线 <-_<-!!!


1 .permission类

权限类表示对系统资源的访问(也就是说你有什么权限)。java.security.Permission是个抽象类,被子类继承以表示特定的访问。例如

Permission perm = new java.io.FilePermission("/poject/123.txt" ,"write")

新权限可以从Permission类或其子类(如java.security.BasicPermission)进行子类化。通常通过将一个或多个字符串参数传递给构造函数来生成每个权限实例。在具有两个参数的常见情况下,第一个参数通常是“目标的名称”,第二个参数是“动作”(action)。通常,一组动作可以指定为逗号分隔的符合字符串。例如上面的就代表对123.txt文件的write权限。



2.java.security.PermissionColleciton,也是一个抽象类
此类保存相同权限的集合,也就是说它里面都是同一个类型的权限,要么都是FilePermission,要么都是BasicPermission,我们以FilePermissionColleciton来举例,为什么是这样的
我们以FilePermission为例
由于FilePermissionCollection的方法不算太多,所以我们仔细的分析一下它是怎么工作的。
1.构造函数FilePermissionCollection()

2.add(Permission)
3.implies(Permission)
解释一下:由于FilePermissionCollection是个权限集合,都是FilePermission,每个FilePermission代表不同对文件的权限,如果这个FilePermissionCollection中
有参数Permission所代表的权限,那么返回true
4.element
迭代FilePermission元素,这里不说

-------重点说下面的两个方法--------------

5.writeObject
6.readObject


没什么说的和writeObject相对。把wirteObject存的读出来。


逻辑图:


其实还有一个有意思的理解:


------这样理解会更加形象,之后我会专门有篇博客写序列化,我序列化理解的也浅


3.java.security.Permissions
也是放Permission的,哪个Permission都可以放进去,不一定像FilePermissionCollection必须放FilePermission


4.java.security.ProtectionDomain
ProtectionDomain类封装了域的特征。这样的域包含一组类,当他们代表给定的一组主体被执行时,他们的实例被授予一组权限。
ProtectionDomain使用CodeSource,ClassLoader,Principals和PermissionCollection。
java sdk中的一部分提供的所有代码被认为是系统代码,在系统域内运行。每个应用程序在其适当的域运行,这是由策略决定的

可以确保任何非系统域中的对象无法发现另一个非系统域中的对象。该分区可以通过类解析和加载来实现,例如,为不同的域使用不同的类加载器
然而,SecureClassLoader(或其子类)岂可在其选择的情况下从不同的域加载类,从而允许这些类在相同的命名空间(由类加载器区分)内共存。




 -------------------我对于这个概念理解的也是费劲----------------

5.java.security.AccessController
AccessController类主要用于三个目的

1.根据当前的有效安全策略来决定是否允许或拒绝对关键系统资源的访问(AccessController.checkPermission(Permission))
2.将代码标记为特权,从而影响随后的访问决定(AccessController.doPrivileged)
3.当前代码的上下文,也就是父线程的访问控制策略(AccessController.getContext)


6.java.security.AccessControllerContext
AccessController的checkPermission方法在当前执行线程的上下文中执行安全检查。但是当这种检查在不同的上下文中进行时,就会出现困难
也就是说,有时在特定的上下文中进行的安全检查实际上许哟啊在不同的上下文中完成。例如,当一个线程将事件发布到另一个线程时,如果服务
请求对控制器的资源访问,则服务请求事件的第二个线程将不具有完成访问控制的适当上下文。
为了解决这个问题,AccessControllerContext上场了,调用AccessController.getContext即可。该AccessControllerContext捕获相关的信息,使得
可以通过从不同上下文检查该上下文信息来访问控制决定。



7.java.security.GuardedObject
当访问控制在不同的上下文进行时,我们用AccessControllerContext。然而,当前线程不能访问上下文的时候怎么办?对于这种情况,就用GuardObject
这个类的大体思想就是先把你想要的对象放在GuardedObject中,然后当把你想要的对象拿出来的时候需要在GuardedObject内部进行一些安全检查才可以--
废话不说,上源码

这个guard是当我们拿出来这个对象的时候,要执行的安全策略


getObject


---------------------第三部分,分析getClassLoader,getContextClassLoader  -------废话不说,上源码

1.getClassLoader


       如果存在SercurityManager,则调用checkClassLoaderPermission




只有想要的ClassLoader是当前调用类ClassLoader的父加载器或者一样的时候,才会执行权限检查



进行权限检查



AccessController.checkPermission我就不深挖了,比较麻烦--------所以说到此,整个的getClassLoader方法分析完毕,下面给出方法的调用栈。

· getClassLoader的方法调用栈:




2.getContextClassLoader ------这个方法可以让你获得父线程的ClassLoader,即使当前线程的加载器和它(父线程的ClassLoader)之间没有父子关系


-------和getClassLoader的过程一样-----




3.重点说一下SercurityManager
通过这个名字可以知道,安全管理器,应该处于中心位置,为什么这么说呢,废话不说,上图


可以看出任何应用程序,(或者说不管哪个类)在进行权限检查的时候,都会用到SecurityManager,而SercurityManager则会用到AccessControlle
所以你也可以直接使用AccessController。




-----------------------------就写这么多

-----------------------------参考 https://docs.oracle.com/javase/6/docs/technotes/guides/security/spec/security-spec.doc1.html#18313,本文逻辑参考这个技术文档





你可能感兴趣的:(java语言)