示例中需要在eclipse中创建2个project:ServiceCentre和TestService
java.policy
permission java.io.FilePermission "c:/TestService-1.0.jar", "read";
permission java.lang.RuntimePermission "createClassLoader";
} ;
grant codeBase " file:/c:/TestService-1.0.jar " {
permission java.io.FilePermission "C:/text.txt", "read";
} ;
Project - ServiceCentre
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
/**
* @author Donf Yang
*/
public class ServiceCentreMain {
public void loadService() {
URL[] urls;
try {
urls = new URL[] { new URL("file:c:/TestService-1.0.jar") };
URLClassLoader ll = new URLClassLoader(urls);
final Class a = ll.loadClass("test.TestService");
Object o = a.newInstance();
Method m = a.getMethod("doService", null);
m.invoke(o, null);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param args
*/
public static void main(String[] args) {
ServiceCentreMain s = new ServiceCentreMain();
s.loadService();
}
}
将TestService打包,放到C盘
import java.io.FilePermission;
import java.security.AccessController;
import java.security.Permission;
/**
* @author Donf Yang
*
*/
public class TestService {
public void doService() {
doFileOperation();
}
private void doFileOperation() {
Permission perm = new FilePermission("C:/text.txt", "read");
AccessController.checkPermission(perm);
System.out.println("TestService has permission");
}
}
运行这个例子的时候,会出现权限错误,把doService()修改一下,就可以顺利通过
// doFileOperation();
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
doFileOperation();
return null;
}
});
}
在这个例子中AccessControlContext的stack顺序为
2. file:/D:/Workspaces/ExchangeConnect_V2_Trunk_Maven_workspace/ServiceCentre/bin/*
1 . file:/c:/TestService-1.0.jar
2没有权限,1有权限,使用doPrivileged后,不检查2
看一下java.security.AccessController的JavaDoc:
其中提到的no further checking is done的意思是指stack中的checking
加入一个TestService2,文件操作在1,stack为(1,2,3为checking顺序)
3 . file:/D:/Workspaces/ExchangeConnect_V2_Trunk_Maven_workspace/ServiceCentre/bin/*
2 . file:/c:/TestService-1.0.jar
1. file:/c:/TestService2-1.0.jar
checking顺序为 1->2->3
如果doPrivileged是在2中调用,那么1,2需要具有权限,3不再进行检查
如果doPrivileged是在1中调用,那么1需要具有权限,2,3不再进行检查
总结:
1. 这里容易理解错误的地方是checking顺序,例如一个调用链 MethodA->MethodB->MethodC(这里的3个方法需要在3个不同的ProtectionDomain中),doPrivileged在MethodB中,很容易理解成检查A,B而不检查C,实际上stack中检查顺序为C->B->A,也就是检查C,B而不检查A
2. ServiceCentre不需要太多权限,而Service就需要使用doPrivileged来避免受到ServiceCentre的权限限制(如果service有足够的权限),Equinox中有很多这样的例子(Equinox扮演Service的角色)。
来自不同的位置的代码可以由一个CodeSource对象描述其位置和签名证书。根据代码的CodeSource的不同,代码拥有不同的权限。例如所有Java SDK自带的代码都具有所有的权限,而Applet中的代码则具有非常受限的权限,用户编写的代码可以自己定制权限(通过SecurityManager)。
当执行一段代码时,这段代码的StackTrace包含了从Main开始所有正在被调用而且没有结束的方法。在这个调用过程中,很有可能出现跨多个不同的CodeSource的调用序列。由于CodeSource不同,这些代码通常拥有不同的权限集。只有所有途经的CodeSource都具有对应的权限集合时,当前正在运行的代码才能存取某个Resource。
而doPrivileged方法是对这个规则的一种补充。他类似于Unix中的setuid程序。Unix中的login程序必须访问password文件从而获得用户授权信息,但是用户不能随意的访问password文件。因此,login程序具有setuid位,它不管被哪个用户所调用,都具有root的权限。
调用doPrivileged的方法不管其StackTrace中其他方法的权限,而仅仅根据当前方法的权限来判断用户是否能访问某个resource。也即可以规定用户只能用某种预定的方式来访问其本来不能访问的resource。
使用doPrivileged方法和使用setuid位都有需要注意的地方,例如仅执行必要的操作。否则,可能带来安全上的问题。