八、类加载器2——实战


如果我们用java平台,也许在安全上我们进了一小步,这一小步对于程序员和平台管理员有一些要求,产生一些额外的工作量,这些日常的工作正如下所介绍



一、让你的程序,或者j2ee运行安全管理器

启动程序VM变量:

-Djava.security.manager -Djava.security.policy=D:/java.policy


我们可以指定多个安全文件.policy文件。只需要修改.security文件配置即可。




二、程序员可以定义自己的类加载器


package ClassLoader;

import java.io.FilePermission;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.CodeSource;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.util.HashMap;

public class myClassLoader extends URLClassLoader {

	public myClassLoader(URL[] urls) {
		super(urls);
	}

	public final synchronized Class loadClass(String name, boolean resolve)
			throws ClassNotFoundException {
		// 要有包的访问权限
		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 {
		// 定义包下class的权限
		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.
		//默认由父加载器设置类的权限,URLClassLoader将会沿用java策略
		//PermissionCollection pc = super.getPermissions(codesource);
		//pc.add(new RuntimePermission("exitVM"));
		//pc.add(pc);
		
		//完全由自己的类加载器指定类权限
		FilePermission fp = new FilePermission("E:/-", "read,execute");
		FilePermission fp1 = new FilePermission("D:/-", "read,execute");
		Permissions ps = new Permissions();
		ps.add(fp);
		ps.add(fp1);
		return ps;
	}

}

以上是我们自定义的类加载器,由他加载的类没有其他的权限,但是有访问D盘 、E盘文件的权限。


我们用他加载一个类,这个类主要用于访问文件,但是我们只希望加载进来的类访问E、D盘的文件,这个类加载器很好的满足了安全要求

一步一步看看!!

先定义一个接口,这个接口主要是用于访问文件,他有一个远程(第三方)的实现类

注意:请注意类加载的层次关系,层次关系不仅安全,对接口的使用也是很有用处的,父类加载器载入接口,子类就能载入接口的实现了,所以接口在应用部署上应该优先考虑,标准的接口应该尽量使用java核心包中接口。


package ClassLoader;

public interface AccessFileInterface {
	public void readFile(String fileName);
}

这个接口我们和myClassLoader放在同一个包中,由应用的加载器加载。


接着,我们在远程创建一个文件访问类,实现上面的接口

我们把接口copy到另外一个项目,并实现如下类

package classloadtest;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import ClassLoader.AccessFileInterface;

public class AccessFile implements AccessFileInterface{
	public void readFile(String fileName){
		File file = new File(fileName);
        InputStream in = null;
        try {
            System.out.println("以字节为单位读取文件内容,一次读一个字节:");
            // 一次读一个字节
            in = new FileInputStream(file);
            int tempbyte;
            while ((tempbyte = in.read()) != -1) {
                System.out.write(tempbyte);
            }
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        try {
            System.out.println("以字节为单位读取文件内容,一次读多个字节:");
            // 一次读多个字节
            byte[] tempbytes = new byte[100];
            int byteread = 0;
            in = new FileInputStream(fileName);
            this.showAvailableBytes(in);
            // 读入多个字节到字节数组中,byteread为一次读入的字节数
            while ((byteread = in.read(tempbytes)) != -1) {
                System.out.write(tempbytes, 0, byteread);
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e1) {
                }
            }
        }

	}
	

    /**
     * 显示输入流中还剩的字节数
     */
    private void showAvailableBytes(InputStream in) {
        try {
            System.out.println("当前字节输入流中的字节数为:" + in.available());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    
    public static void main(String[] args) {
    	AccessFile a = new AccessFile();
    	a.readFile("D:\\testread.txt");
	}
	
}


这个类实现了接口,并不能访问任何的文件,(因为java默认的配置策略赋予了他最大的权限——默认是没文件权限的),此时运行程序会抛出不能访问文件的异常,

如下的异常:


以字节为单位读取文件内容,一次读一个字节:
Exception in thread "main" java.security.AccessControlException: access denied ("java.io.FilePermission" "D:\testread.txt" "read")
	at java.security.AccessControlContext.checkPermission(AccessControlContext.java:372)
	at java.security.AccessController.checkPermission(AccessController.java:559)
	at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
	at java.lang.SecurityManager.checkRead(SecurityManager.java:888)
	at java.io.FileInputStream.<init>(FileInputStream.java:135)
	at classloadtest.AccessFile.readFile(AccessFile.java:17)
	at classloadtest.AccessFile.main(AccessFile.java:66)




为了让这个类有权访问文件,我们可以让myclassloader加载它

接下来,我们做一个应用,用myclassloader加载这个文件访问类

package ClassLoader;

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Date;

public class test1 {
	
	
	public static void main(String[] args) {
		
		URL[] urls = new URL[1];
		try {
			urls[0] = new URL("file:/E:/workspace_20140601/classloadtest/bin/");
			
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		try {
			//long t_s = (new Date()).getTime();
			URLClassLoader ucl = new myClassLoader(urls);
			//ucl.loadClass("classloadtest.AccessFile");
			//ucl.loadClass("classloadtest.AccessFile1");
			//long t_s2 = (new Date()).getTime();
			//System.out.println(t_s-t_s2);
			Class<?> c = ucl.loadClass("classloadtest.AccessFile");
			AccessFileInterface acc = (AccessFileInterface)(c.newInstance());
			acc.readFile("d:/testread.txt");
			//long t_s3 = (new Date()).getTime();
			//System.out.println(t_s-t_s3);
			Class<?> c1 = ucl.loadClass("classloadtest.AccessFile1");
			AccessFileInterface acc1= (AccessFileInterface)(c1.newInstance());
			acc1.readFile("e:/testread1.txt");
			//long t_s4 = (new Date()).getTime();
			//System.out.println(t_s-t_s4);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}
}


这个应用使用myClassLoader加载Accessfile类, 并给这个类相应的权限-----d、e盘所有的访问权限,

现在这个类能够访问所有的d、e盘下的文件了,没有异常发生了,另外由这个加载器加载的其他类都有这个权限了。


可以说,这个类加载器的所有URL资源被加载后,都会得到相应的文件访问权限,除非这个资源路径被父加载器覆盖。

另外,要注意,类加载赋予类权限的代码是特权代码。也许这就是漏洞哦,没有绝对的安全,就像你信任你的朋友,但是朋友也可能会坑害你一样,人为因素才是安全的伦理。


所以,我们最好是只读取签名的jar,提供完善的代码源,这样就能设定——谁签名的jar有多大的权限,这样我们能在平台上更方便引用第三放的类库,并设置安全了,虽然不能做到绝对安全,但是如果大家都有共同的利益所在,估计是不会让原子弹提前爆发的。






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值