jvm 安全管理器(转载)

原文:java之jvm学习笔记四(安全管理器)

      java之jvm学习笔记六(实践写自己的安全管理器)

 

  前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器。

安全管理器是一个单独的对象,在java虚拟机中,它在访问控制-对于外部资源的访问控制-起到中枢作用。如果光看概念可能并不能很好的理解,或者说比较抽象,

下面是ClassLoader其中的一个构造函数,先简单的看看它在初始化ClassLoader之前会做一些什么操作            

protected ClassLoader(ClassLoader parent) {
  SecurityManager security = System.getSecurityManager();
  if (security != null) {
    security.checkCreateClassLoader();
  }
  this.parent = parent;
  initialized = true;
}

这个构造函数的第一话(当然还有隐式调用)就是System.getSecurityManager();这行代码返回的就是一个安全管理器对象security,这个对象所属的目录为java.lang.SecurityManager。

这个构造函数先判断如果已经安装了安全管理器security (在前面类装载器的章节,我们提到过,类装载器和安全管理器是可以由用户定制的,在这里有了体现吧!!既然有System.getSecurityManager();

你当然也应该猜到有System.setSecurityManager();),也就是安全管理器不为空,那么就执行校验,跳到checkCreateClassLoader();看看他做的是什么操作

public void checkCreateClassLoader() {
  checkPermission(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);
}

这里又调用了另外一个方法,从方法名字上,就可以猜到这个方法是用来校验权限的,校验是否有创建ClassLoader的权限,再跳到checkPermisson方法里

public static void checkPermission(Permission perm) throws AccessControlException 
{
  //System.err.println("checkPermission "+perm);
  //Thread.currentThread().dumpStack(); if (perm == null) {
  throw new NullPointerException("permission can't be null");
} AccessControlContext stack = getStackAccessControlContext();
  // if context is null, we had privileged system code on the stack.
  if (stack == null) {
    Debug debug = AccessControlContext.getDebug();
    boolean dumpDebug = false;
    if (debug != null) {
      dumpDebug = !Debug.isOn("codebase=");
      dumpDebug &= !Debug.isOn("permission=") ||
      Debug.isOn("permission=" + perm.getClass().getCanonicalName());
    } 
  if (dumpDebug && Debug.isOn("stack")) {     Thread.currentThread().dumpStack();   } if (dumpDebug && Debug.isOn("domain")) {     debug.println("domain (context is null)");   } if (dumpDebug) {     debug.println("access allowed "+perm);   }   return; } AccessControlContext acc = stack.optimize();   acc.checkPermission(perm); } }

上面的这个方法有些代码比较难以理解,我们不用每行都读懂(这个方法涉及的东西比较多,它涉及到了代码签名认证,策略还有保护域,这些我们在后一节中会详细的讲解,

看不懂先跳过),看它的注解// if context is null, we had privileged system code on the stack.意思就是如果当前的访问控制器上下文为空,在栈上的系统代码将得到特权,

找到acc.checkPermission(perm);再跳进去找到下面这段代码

/*
* iterate through the ProtectionDomains in the context.
* Stop at the first one that doesn't allow the
* requested permission (throwing an exception).
*
*/ /* if ctxt is null, all we had on the stack were system domains,
or the first domain was a Privileged system domain. This
is to make the common case for system code very fast */ if (context == null)
return; 

for (int i=0; i< context.length; i++) {   if (context[i] != null && !context[i].implies(perm)) {     if (dumpDebug) {       debug.println("access denied " + perm);     } if (Debug.isOn("failure") && debug != null) {     // Want to make sure this is always displayed for failure,     // but do not want to display again if already displayed     // above.     if (!dumpDebug) {       debug.println("access denied " + perm);     }     Thread.currentThread().dumpStack();     final ProtectionDomain pd = context[i];     final Debug db = debug;     AccessController.doPrivileged (new PrivilegedAction() {       public Object run() {         db.println("domain that failed "+pd);       return null;     }   }); } throw new AccessControlException("access denied "+perm, perm); } }

什么都不用看,就看最上面的那段注解,意思是遍历上下文中的保护域,一旦发现请求的权限不被允许,停止,抛出异常,到这里我们有一个比较清晰的概念了,安全管理器就是用来控制执行权限的

而上面的这段代码中有一个很重要的类 AccessController,访问控制器,还有一个很重要的名词保护域(保护域我们在前面一节也有简单的带过一下,是不是有点印象),这些可能现在听有点模糊,不要担心,

暂时不要管,后面一章节慢慢的会对他们进行讲解。


  好了了解安全管理器是做什么的之后,接下来,来做一个下的实验,先来验证,默认安全管理是没有被安装的,接着来试着把他安装上去。在我的环境中我是没有安装默认的安全管理器的,

也没有基于默认的安全管理器写自己的安全管理器,如果需要打开的话,可以在程序显示的安装安全管理器,同样可以让它自动安装默认的安全管理器 (给jvm加上-Djava.security.manager就可以了。

下面我们用熟悉的ecplise写一个简单的demo来看看安装前后的区别,在下一节中,会详细的来学习代码签名认证和策略,并写一个自己的安全管理器。

public static void main(String[] args) {
  System.out.println(System.getSecurityManager());
}

运行这个main函数,输出什么?是的输出null,这个时候我们没有安装默认的安全管理器

重新换个方式运行,在ecplise里右键--Run As--Run Configuration--Arguments,在VM arguments的栏目里输入

-Djava.security.manager。在点击Run,这个时候看到什么?

输出:securityManager的对象名。这个时候默认的安全管理器就被安装上了。

总结:

      在java虚拟机中,它在访问控制-对于外部资源的访问控制-起到中枢作用

 

安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用AccessController的checkPerssiom方法,

访问控制器AccessController的栈检查机制又遍历整个PerssiomCollection来判断具体拥有什么权限一旦发现栈中一个权限不允许的时候抛出异常否则简单的返回,

这个过程实际上比我的描述要复杂得多,这里我只是简单的一句带过,因为这里涉及到很多比较后面的知识点。 

 下面来尝试一下写一个非常简单的demo,旨在让你有一个比较形象的思维,不会在概念上打转。

        第一步,定义一个类继承自SecurityManger重写它的checkRead方(如果你有兴趣可以先跳到super.checkRead(file, context);看看,当然不看也没有关系,

我们后面的章节会基于这个demo做扩展的时候也会讲到)。

package com.yfq.test;

public class MySecurityManager extends SecurityManager {

@Override
public void checkRead(String file) {
  //super.checkRead(file, context);
  if (file.endsWith("test")) 
  throw new SecurityException("你没有读取的本文件的权限"); 
  }
}

   第二步,定义一个有main函数的public类来验证自己的安全管理器是不是器作用了。

package com.yfq.test;

import java.io.FileInputStream;
import java.io.IOException;

public class TestMySecurityManager {
  public static void main(String[] args) {
    System.setSecurityManager(new MySecurityManager());
    try {
      FileInputStream fis = new FileInputStream("test");
      System.out.println(fis.read());
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

 第三步,运行代码查看控制台输出

Exception in thread "main" java.lang.SecurityException: 你没有读取的本文件的权限
 at com.yfq.test.MySecurityManager.checkRead(MySecurityManager.java:9)
 at java.io.FileInputStream.<init>(FileInputStream.java:100)
 at java.io.FileInputStream.<init>(FileInputStream.java:66)
 at com.yfq.test.TestMySecurityManager.main(TestMySecurityManager.java:10)

 从上面的异常我们发现,安全管理器起作用了。读过笔记四的人应该会发现,这里我们用到了一个笔记四提到的方法:System.setSecurityManager(new MySecurityManager());

这个是安装安全管理器的另外一种方法,笔记四中我们曾经用-Djava.security.manager安装过默认的安全管理器,有印象不?

拓展内容:

  好了,我们的安全管理器是怎么被执行的呢?如果你有兴趣可以继续往下看一下,也可以跳过,这里只是简单的介绍一下,也是本人习惯的学习思路

直接跳到FileInputStream的构造函数里,下面贴出代码,简单阅读一下

public FileInputStream(File file) throws FileNotFoundException {
  String name = (file != null ? file.getPath() : null);
  SecurityManager security = System.getSecurityManager();
  if (security != null) {
    security.checkRead(name);
  }
  if (name == null) {
    throw new NullPointerException();
  }
  fd = new FileDescriptor();
  open(name);
}

发现没?它首先执行SecurityManager security = System.getSecurityManager();,然后再调用security的checkRead方法,就是这么简单。

如果你还有兴趣那么继续往下读,在使用java的File时,你是否用过setWritable(boolean, boolean),让你可以指定创建文件的权限,学习了安全管理器之后你有没有有豁然开朗的感觉,它是怎么实现的,

相信你已经猜到了,没有错就是安全管理器设置权限啦。下面贴出它的代码,同时也引入一个新的概念Permission

public boolean setWritable(boolean writable, boolean ownerOnly) {
  SecurityManager security = System.getSecurityManager();
  if (security != null) {
    security.checkWrite(path);
  }
  return fs.setPermission(this, FileSystem.ACCESS_WRITE, writable, ownerOnly);
}

Permisson就是权限的意思,它仅仅取出安全管理器然后将文件的权限设置了一下而已,这个也是后面所有关于权限的一个不可或缺的类!

好了今天的文件安全管理器demo就到这里。意在浅显易懂!

 

申明:文章的部分内容有参照网上的其他作者的内容,这里只用来学习交流!

 

posted @ 2019-04-18 18:46  青青子衿J  阅读(271)  评论(0)    收藏  举报