如果我们用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有多大的权限,这样我们能在平台上更方便引用第三放的类库,并设置安全了,虽然不能做到绝对安全,但是如果大家都有共同的利益所在,估计是不会让原子弹提前爆发的。