示例中需要在eclipse中创建2个project:ServiceCentre和TestService java.policy
grant codeBase
"
file:/D:/Workspaces/ExchangeConnect_V2_Trunk_Maven_workspace/ServiceCentre/bin/*
"
{ 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
package
test;
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(); } }
Project - TestService
将TestService打包,放到C盘
package
test;
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()修改一下,就可以顺利通过
public
void
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:
A caller can be marked as being
"
privileged
"
(see doPrivileged and below). When making access control decisions, the checkPermission method stops checking
if
it reaches a caller that was marked as
"
privileged
"
via a doPrivileged call without a context argument (see below
for
information about a context argument). If that caller
'
s domain has the specified permission, no further checking is done and checkPermission returns quietly, indicating that the requested access is allowed. If that domain does not have the specified permission, an exception is thrown, as usual.
其中提到的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位都有需要注意的地方,例如仅执行必要的操作。否则,可能带来安全上的问题。