zoukankan      html  css  js  c++  java
  • java jvm学习笔记十二(访问控制器的栈校验机制)

    欢迎装载请说明出处:http://blog.csdn.net/yfqnihao

                               本节源码:http://download.csdn.net/detail/yfqnihao/4863854

                               这一节,我们会简单的描述一下jvm访问控制器的栈校验机制。

                               这节课,我们还是以实践为主,什么是栈校验机制,讲一百遍不如你自己实际的代码一下然后验证一下,下面我们下把环境搭起来。

                               第一步,配置系统环境。(copy吧,少年)

     
    1. path=%JAVA_HOME%/bin  
    2. JAVA_HOME=C:/Java/jdk1.6.0_01  
    3. CLASSPATH=.;%JAVA_HOME%/lib/dt.jar;%JAVA_HOME%/lib/tools.jar      

                               第二步,配置一个策略文件的运行环境。

    第一个类Doer:

    package com.yfq.test;
    
    public abstract interface Doer {
        void doYourThing();
    }


    第二个类Friend:

    package com.yfq.test.friend;
    
    import java.security.AccessController;
    import java.security.PrivilegedAction;
    
    import com.yfq.test.Doer;
    
    public class Friend implements Doer{
        private Doer next;
        private boolean direct;
        
        public Friend(Doer next,boolean direct){
            this.next=next;
            this.direct=direct;
        }
        
        @Override
        public void doYourThing() {
            System.out.println("Im a Friend");
    
            if (direct) {
                next.doYourThing();
            } else {
                AccessController.doPrivileged(new PrivilegedAction() {
    
                    @Override
                    public Object run() {
                        next.doYourThing();
                        return null;
                    }
    
                });
    
            }
        }
    }


    第三个类Stranger:

      

    package com.yfq.test.stranger;
    
    import java.security.AccessController;
    import java.security.PrivilegedAction;
    
    import com.yfq.test.Doer;
    
    public class Stranger implements Doer {
    
        private Doer next;
        private boolean direct;
    
        public Stranger(Doer next, boolean direct) {
            this.next = next;
            this.direct = direct;
        }
    
        @Override
        public void doYourThing() {
            System.out.println("Im a Stranger");
    
            if (direct) {
                next.doYourThing();
            } else {
                AccessController.doPrivileged(new PrivilegedAction() {
    
                    @Override
                    public Object run() {
                        next.doYourThing();
                        return null;
                    }
    
                });
    
            }
        }
    
    
    }

    第四个类TextFileDisplayer:

    import java.io.CharArrayWriter;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    
    import com.yfq.test.Doer;
    
    
    public class TextFileDisplayer implements Doer{
        String fileName;
        public TextFileDisplayer(String fileName){
            this.fileName=fileName;
        }
        @Override
        public void doYourThing() {
            try {
                FileReader fr = new FileReader(fileName);
                try {
                CharArrayWriter caw = new CharArrayWriter();
                int c;
                    while((c=fr.read())!=-1){
                        caw.write(c);
                    }
                    System.out.println(caw.toString());
                } catch (IOException e) {
                    e.printStackTrace();
                }finally{
                    if(fr!=null){
                        try {
                            fr.close();
                            fr=null;
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        
                    }
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            
        }
    
    }

                                    第三步,参考笔记http://blog.csdn.net/yfqnihao/article/details/8267669,把Friend和Stranger打包并签名,并放到ecplise编译目录bin/jars下,把生成的密钥存储文件放在与bin同级的目录下。(你也可以先用我上传的源码里的jar包,不过还是建议你动手练一练)

                                     第四步,配置策略文件

    keystore "ijvmkeys.keystore";
    
    grant signedby "friend.keystore" {   
        permission java.io.FilePermission "d:/answer.txt", "read"; 
        permission java.io.FilePermission "d:/question.txt", "read";  
    }; 
    
    grant signedby "stranger.keystore" {   
           permission java.io.FilePermission "d:/question.txt", "read"; 
    }; 
     
    
    grant codeBase "file:D:/workspace/MyAccessControlerStack/bin/*" {   
        permission java.io.FilePermission "d:/answer.txt", "read"; 
        permission java.io.FilePermission "d:/question.txt", "read"; 
    }; 

                                        第五步,新建一个类,这个类里有个主函数,用于校验类Friend,Stranger,TextFileDisplayer对于question.txt的读取权限

    import com.yfq.test.friend.Friend;
    import com.yfq.test.stranger.Stranger;
    
    
    public class Example2 {
        public static void main(String[] args) {
            TextFileDisplayer tfd=new TextFileDisplayer("d:/question.txt");
            Friend friend = new Friend(tfd,true);
            Stranger stranger = new Stranger(tfd,true);
            stranger.doYourThing();
        }
    }


                                     第六步运行,cmd窗口输入:
    java -classpath .;jars/friend.jar;jars/stranger.jar -Djava.security.manager -Djava.security.policy=D:/workspace/MyAccessControlerStack/src/myPolicy.txt Example2

       

    说明:

    从这里,我们并不能很直观的发现访问控制器的栈校验机制,看Example2的main函数,我们知道当stranger执行doYourThing的时候,会经过这么一个过程,

    Example2--------->被ApplClassLoader装入到ProtectionDomian_Example2中---------------->执行main函数

    TextFilDisplayer------->ApplClassLoader判断当前的线程有没有装载类TextFilDisplayer的权限---------->装载到ProtectionDomian_TextFileDisplayer中

    Friend------->ApplClassLoader判断当前的线程有没有装载类TextFilDisplayer的权限-------->装载到ProtectionDomian_Friendr中()

    Stranger------->ApplClassLoader判断当前的线程有没有装载类TextFilDisplayer的权限----------->装载到ProtectionDomian_Stranger中

    Stranger的实例对象stranger执行doYourThing方法---->直接调用Friend的实力引用执行doYourThing方法----->Friend的实例引用直接调用TextFileDisplayer的doYourThing方法

    输出question.txt的文本内容。

    这个过程中,AccessControler到底是在什么时候执行的,怎么执行的呢,来看下面这个图

     

    上面的这个图是一个AccessControlerContext,也就是访问控制器上下文,它大概了描述了,各个函数被调用的时候的保护域的压栈过程,直到栈顶结束压栈之后,它会按照先进后出的规则,AccessControler调用自己的checkPermission方法,检验每一层的权限(上面的保护域数组中,名为BOOTSTRAP保护域是系统保护域,它的权限是SecurityConstants.ALL_PERMISSION,这就意味着他什么都能够做)。AccessControler的保护域数组成员则会调用自己的implies方法,ProtectionDomain的implies方法会先查看是否有配置了策略文件,如果有的话就将当前保护域传递给Policy这个单例,由他从配置文件中取出PermissionCollection然后再调用每个Permission检验它的implies方法,如果没有设定特定的配置文件,则直接调用当前保护域中的PermissionCollecion成员的implies,再由它调用Permission的implies方法。

    由于Examples2所读取的是question.txt文本,又由于我们的策略文件中,让Friend,Stranger,TextFileDisplayer都拥有它的读取权限,所以顺利的执行了。

                                          第七步:为了验证我们的猜想是正确的,我们现在修改Example2如下

                                          

    import com.yfq.test.friend.Friend;
    import com.yfq.test.stranger.Stranger;
    
    
    public class Example {
        public static void main(String[] args) {
            TextFileDisplayer tfd=new TextFileDisplayer("d:/answer.txt");
            Friend friend = new Friend(tfd,true);
            Stranger stranger = new Stranger(tfd,true);
            stranger.doYourThing();
        }
    }

    这里我们仅仅是将question.txt换成了answer.txt,而关于这个文件我们知道Stranger是没有读取的权限的,下面我们来运行它看看

                                            第八步,cmd窗口输入java -classpath .;jars/friend.jar;jars/stranger.jar -Djava.security.manager -Djava.security.policy=D:/workspace/MyAccessControlerStack/src/myPolicy.txt Example
         

    我们再来看AccessControlerContext的图

    前面的安全检查都通过了,但是到了STRANGER保护域的时候,由于Stranger'没有读取answer.txt的权限,所以implies方法抛出了一个AccessControlException。

    那么AccessControler的栈校验机制能够带来什么好处呢??

    答案很显然,就好像我们第七步一样,我们试图让一个没有权限的类来调用一个具有高级权限的类别,以达到“破坏”的目的,由于栈校验机制的存在,让我们的这种幻想变得不容易实现,但是不容易实现并不代表不能够实现,下面我们将来学习一个方法,这个方法叫doPrivileged(),这个方法可以帮助我们达到第七步的目的。

                                       第九步,修改我们上面的Example类如下

    import com.yfq.test.friend.Friend;
    import com.yfq.test.stranger.Stranger;
    
    
    public class Example3 {
        public static void main(String[] args) {
            TextFileDisplayer tfd=new TextFileDisplayer("d:/answer.txt");
            Friend friend = new Friend(tfd,false);
            Stranger stranger = new Stranger(friend,true);
            stranger.doYourThing();
        }
    }

    我们只是将friend的初始化参数做了稍微的调整,new Friend(tfd,true)改为了new Friend(tfd,false);这个调整使得friend的doYourThing方法不是直接的执行next.doYourThing()而是通过给AccessController.doPrivileged()方法传入一个匿名内部类并重写它的run方法,在run方法里调用了next.doYourThing()。

                                          第十步,然后我们在cmd窗口输入:java -classpath .;jars/friend.jar;jars/stranger.jar -Djava.security.manager -Djava.security.policy=D:/workspace/MyAccessControlerStack/src/myPolicy.txt Example3


    查看输出:既然成功由没有权限查看answer.txt的Strange完成了查看answer.txt的操作。这是怎么回事??我们再来看刚才表示AccessControlContext的图

    由于我们在Friend中安装了doPrivileged(),所以doPrivileged()这个方法被压入栈而且是在Stranger的前面,doPrivileged()执行的时候会调用我的匿名内部类Friend$1并执行它的run方法,而run方法里执行完next.doYourThing之后,AccessControlContext将继续执行判断到doPrivileged(),它发现这是一个BootStrap的调用,那么AccessControlContext会继续执行另外一个判断,判断是谁安装了这个doPrivileged()方法,所以执行到了Freind的doYourThing(),判定它有打开answer.txt的权限,那么最后才直接把run方法的return返回出去。

    就是通过这样的方式,使得我们没有权限的Stranger能够“越权"操作。

    但是越权还是有条件的,如第九步,我们执行”越权“方法run的方法栈帧是嵌套在Friend的doYourThing的线程栈帧中的,由于Friend有读取answer.txt的权限,这才使得run方法有了”越狱“的机会。

                                       第十一步,我们修改一下Example3来验证一下自己的观点

    import com.yfq.test.friend.Friend;
    import com.yfq.test.stranger.Stranger;
    
    
    public class Example4 {
        public static void main(String[] args) {
            TextFileDisplayer tfd=new TextFileDisplayer("d:/answer.txt");
            Stranger stranger = new Stranger(tfd,false);
            Friend friend = new Friend(stranger,true);
            stranger.doYourThing();
        }
    }

                                          第十二步,cmd窗口输入java -classpath .;jars/friend.jar;jars/stranger.jar -Djava.security.manager -Djava.security.policy=D:/workspace/MyAccessControlerStack/src/myPolicy.txt Example4

    报出异常了,这个异常就是由于stranger$1这个内部类的方法栈帧是嵌套在stranger的doYourThing的方法栈帧中,而stranger的保护域规定了stranger这个类的对象是没有权限读取answer.txt这个文件,所以run这个方法也就没办法”越狱“。

    总结:

                这一节,我们学习了访问控制器校验保护域权限的过程,它采取的是栈的校验机制(先进后出),而它的每个方法调用总是线程栈帧相关的,如果我们必须要”越狱“,那预约的条件要求调用doPrivileged()方法的栈帧的至少要有执行越狱操作的权限。

  • 相关阅读:
    【LOJ】#3034. 「JOISC 2019 Day2」两道料理
    vue学习笔记(七)组件
    vue学习笔记(五)条件渲染和列表渲染
    vue学习笔记(一)入门
    JavaScript学习总结之函数
    JavaScript学习总结之对象的深拷贝和浅拷贝
    javascript学习总结之Object.assign()方法详解
    ES6学习总结之变量的解构赋值
    ES6学习总结之let和const命令
    javascript学习总结之Object.keys()方法详解
  • 原文地址:https://www.cnblogs.com/lijia0511/p/4973905.html
Copyright © 2011-2022 走看看