七,java类装载器

前面我们说沙箱就相当于存取控制器,java类载入器对于存取控制器来说是重要的依赖,代码基的权限集合是由类装载器创建的


话题:类加载在安全操作上的用处:

----能为每个类建立保护域。如在http://www.hkc.edu.cn/java/class/test.jar URL 上载入类字节码,http://www.hkc.edu.cn/java/class/test.jar 和jar的签名机构对应的证书(证书可以来自其他的证书库)将会构造代码基,同时类加载器还会为这个代码基建立权限集集合(Permissions类型对象)

----能结合虚拟机定义名称空间

----可以让类加载器调用安全管理器,从而限制只能访问适当的类


一,名称空间和类加载器

//略

二,类载入体系结构

//略


三,实现类加载器

1,类装载器的主要方法

public Class loadClass(String name):唯一的公共方法,

原则:先访问父类是否加载了类,这对于安全是至关重要的,

例如:如果没有这个原则,我们就能编写一个String类 替换掉lang包的String,这是JAVA语言安全不允许的


protected Class findClass(String name)

创建对象大部分工作都由此方法完成

---选择合适的机制完成类载入:ftp ,http,file等方式

---负责创建与类相关的保护域

---调用difinaClass()创建对象



protected defindClass(String name,byte[] b,int off,int len)

处理一组字节码,设置类权限(根据findClass得到的保护域)等


protected PermissionCollection getPermissions(CodeSource cs)

注意:Permissions也是PermissionCollection的子类

默认调用Policy类的getPermissions()方法

对开发人员,我们可以覆盖这个方法,从而放弃默认的Policy类和java默认的配置策略,另外我们还能编写自己的Policy类达到同样的目的,后者很复杂,如何编写自己的Policy类详见 六,存取控制器一章


2,使用URL类载入器

以下例子为自定义的类加载器

import java.net.*;
import java.security.*;

public class CustomURLClassLoader extends URLClassLoader {

    static URL urls[];

    static {
        try {
            urls = new URL[2];
            urls[0] = new URL("http://piccolo.East/~sdo/");
            urls[1] = new URL("file:/home/classes/LocalClasses.jar");
        } catch (Exception e) {
            throw new RuntimeException("Can't create URLs " + e);
        }
    };

    public CustomURLClassLoader() {
        super(urls);
    }

    public final synchronized Class loadClass(String name, boolean resolve)
                                 throws ClassNotFoundException {
        // First check if we have permission to access the package.
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            int i = name.lastIndexOf('.');
            if (i != -1) {
                sm.checkPackageAccess(name.substring(0, i));
            }
        }
        return super.loadClass(name, resolve);
    }

    protected Class findClass(final String name)
                    throws ClassNotFoundException {
        // First check if we have permission to access the package.
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            int i = name.lastIndexOf('.');
            if (i != -1) {
                sm.checkPackageDefinition(name.substring(0, i));
            }
        }
        return super.findClass(name);
    }

    protected PermissionCollection getPermissions(CodeSource codesource) {
        // Use all the standard permissions, plus allow the code to
        // exit the VM.
        PermissionCollection pc = super.getPermissions(codesource);
        pc.add(new RuntimePermission("exitVM"));
        return pc;
    }
}


3,使用SecureClassLoader类

如果我们不使用http 文件方式,而是从数据库加载字节码,我们需要继承此类来自定义加载器

例子:

import java.net.*;
import java.security.*;
import java.io.*;

public class CustomSecureClassLoader extends SecureClassLoader {

    public final synchronized Class loadClass(String name, boolean resolve)
                                 throws ClassNotFoundException {
        // First check if we have permission to access the package.
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            int i = name.lastIndexOf('.');
            if (i != -1) {
                sm.checkPackageAccess(name.substring(0, i));
            }
        }
        System.out.println("loading class " + name);
        return super.loadClass(name, resolve);
    }

    protected Class findClass(final String name)
                               throws ClassNotFoundException {
        // First check if we have permission to access the package.
        // You could remove these 7 lines to skip the optional step 4.
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            int i = name.lastIndexOf('.');
            if (i != -1) {
                sm.checkPackageDefinition(name.substring(0, i));
            }
        }
        // Now read in the bytes and define the class
        try {
            return (Class)
                AccessController.doPrivileged(
                    new PrivilegedExceptionAction() {
                        public Object run() throws ClassNotFoundException {
                            byte[] buf = null;
                            try {
                                buf = readClassBytes(name);
                            } catch (Exception e) {
                                throw new ClassNotFoundException(name, e);
                            }
                            CodeSource cs = getCodeSource(name);
                            Class c = defineClass(name, buf,
                                               0, buf.length, cs);
		            System.out.println("defining class " + c);
		            return c;
                        }
                    }
                );
        } catch (java.security.PrivilegedActionException pae) {
            throw (ClassNotFoundException) pae.getException();
        }
    }

    protected byte[] readClassBytes(String name) {
        try {
            // This is the standard technique to read a class
            // from the file system; you must convert the . in 
            // the package name to the directory structure from
            // which you're reading.
            name = name.replace('.', File.separatorChar);
            // We read classes from the directoy foo (plus the
            // additional directories required by the package name).
            DataInputStream dis = new DataInputStream(
                       new FileInputStream("foo" +
                            File.separatorChar + name + ".class"));
            byte[] buf = new byte[dis.available()];
            dis.readFully(buf);
            return buf;
        } catch (IOException ioe) {
            ioe.printStackTrace();
            return null;
        }
    }

    protected CodeSource getCodeSource(String name) {
        try {
            return new CodeSource(new URL("file", "localhost", name),new  java.security.cert.Certificate[0]);
            
        } catch (MalformedURLException mue) {
            mue.printStackTrace();
        }
        return null;
    }

    protected PermissionCollection getPermissions(CodeSource codesource) {
        PermissionCollection pc = new Permissions();
        pc.add(new RuntimePermission("exitVM"));
        return pc;
    }
}


仔细看上面的例子的findClass方法,  我们可以发现类加载器加载字节码以及定义类的流程都在特权域里面,其实URLClassLoader类也是一样用的特权,关于特权接口前面详细介绍过,请参考六,存取控制器章节

      为什么要用特权代码,理解这一点很重要,如果不用特权,类加载器能否加载类将取决于用户的代码,这就要求管理员或者开发人员为每一个java代码都配置策略,只有这样,类加载才能得到访问网路或者文件等等操作,这是一件很庞大的工程,可以说是不可能完成的,想一下你在java.policy中对所有的代码都配置具体的权限,可能吗?

      所以,类加载一般都是要在特权区执行,

      其实,如果加载进来的代码我们认为不可信任,我们只要在策略文件中为这些代码基(或签名)做权限限定就行了。就算他被加载进来了,也会在受限的环境中运行。当然 ,类的权限可以不来自策略,你可以在类加载的时候通过getPermissions方法进行设定,但是此方法实在findclass()中完成的,他并不是特权代码,所以类的权限还是受到你的调用代码权限的制约。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目 次 前言 ..............................................................................................................................................................3 1 范围 11 2 规范性引用文件 11 3 术语和定义 11 4 排版规范 12 4.1 规则 12 1.*程序块要采用缩进风格编写,缩进的空格数为4个。 12 2.*分界符(如大括号‘{’和‘}’)应各独占一行并且位于同一列,同时与引用它们的语句左对齐。在函数体的开始、和接口的定义、以及if、for、do、while、switch、case语句中的程序都要采用如上的缩进方式。 12 3.*较长的语句、表达式或参数(>80字符)要分成多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要进行适当的缩进,使排版整齐,语句可读。 13 4.*不允许把多个短语句写在一行中,即一行只写一条语句 13 5.*if, for, do, while, case, switch, default 等语句自占一行,且if, for, do, while等语句的执行语句无论多少都要加括号{}。 13 6.*相对独立的程序块之间、变量说明之后必须加空行。 13 7.*对齐只使用空格键,不使用TAB键。 14 8.*在两个以上的关键字、变量、常量进行对等操作时,它们之间的操作符之前、之后或者前后要加空格;进行非对等操作时,如果是关系密切的立即操作符(如.),后不应加空格。 14 4.2 建议 15 1.属性和方法不要交叉放置,不同存取范围的属性或者方法也尽量不要交叉放置。 15 5 注释规范 16 5.1 规则 16 1.一般情况下,源程序有效注释量必须在30%以上。 16 ......
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值