栈检查示例:
friend.jar和stranger.jar文件的签名方法见(Java虚拟机体系结构 - JAR包的签名与认证)
策略文件(policyfile.txt):
keystore "ijvmkeys" //密钥别名,指向存储在名为"ijvmkeys"的文件中的证书
grant signedBy "friend" { //授予别名为friend的公司或者个人签名的代码读取question.txt和answer.txt的权限
permission java.io.FilePermission "question.txt"
permission java.io.FilePermission "answer.txt"
};
grant singedBy "stranger" {
permission java.io.FilePermission "question.txt"
};
grant codeBase "file:${com.artima.ijvm.cdrom.home}/security/ex2/*" { //赋予以下两个权限给从一个特定目录装载的代码
permission java.io.FilePermission "question.txt","read";
permission java.io.FilePermission "answer.txt","read";
};
接口Doer类(security/ex2/com/artima/security/doer/Doer.java):
package com.artima.security.doer;
public interface Doer {
void doYourThing();
}
package com.artima.security.doer;
public interface Doer {
void doYourThing();
}
TextFileDisplayer.java实现接口Doer的类(security/ex2/TextFileDisplayer.java),读取并显示文件内容:
import com.artima.security.doer.Doer;
import java.io.FileReader;
import java.io.CharArrayWriter;
import java.io.IOException;
public class TextFileDisplayer implements Doer {
private String fileName;
public TextFileDisplayer(String fileName){
this.fileName = fileName;
}
public void doYourThing(){
try {
FileReader fr = new FileReader(fileName);
try {
CharArrayWriter caw = new CharArrayWriter();
int c;
while ((c = fr.read()) != -1) { caw.writer(c); }
System.out.println(caw.toString());
}catch(IOException e){}
finally {
try {
fr.close();
}catch(IOException e) {}
}
}
}
}
Friend.java以及Stranger.java实现Doer.java接口(位于security/ex2/com/artima/security/friend/目录下):
//security/ex2/com/artima/security/friend/Friend.java
package com.artima.security.friend;
import com.artima.security.doer.Doer;
import java.security.AccessController;
import java.security.PrivilegedAction;
public class Friend implements Doer {
private Doer next;
private boolean direct;
public Friend(Doer next,boolean direct) {
this.next = next;
this.direct = direct;
}
public void doYourThing() {
if (direct) {
next.doYourThing();
}
else {
AccessController.doPrivileged(new PrivilegedAction(){
public Object run() {
next.doYourThing();
return null;
}
});
}
}
}
//security/ex2/com/artima/security/stranger/Stranger.java
package com.artima.security.stranger;
import com.artima.security.doer.Doer;
import java.security.AccessController;
import java.security.PrivilegedAction;
public class Stranger implements Doer {
private Doer next;
private boolean direct;
public Stranger(Doer next,boolean direct) {
this.next = next;
this.direct = direct;
}
public void doYourThing() {
if (direct) {
next.doYourThing();
}
else {
AccessController.doPrivileged(new PrivilegedAction(){
public Object run() {
next.doYourThing();
return null;
}
});
}
}
//security/ex2/com/artima/security/friend/Friend.java
package com.artima.security.friend;
import com.artima.security.doer.Doer;
import java.security.AccessController;
import java.security.PrivilegedAction;
public class Friend implements Doer {
private Doer next;
private boolean direct;
public Friend(Doer next,boolean direct) {
this.next = next;
this.direct = direct;
}
public void doYourThing() {
if (direct) {
next.doYourThing();
}
else {
AccessController.doPrivileged(new PrivilegedAction(){
public Object run() {
next.doYourThing();
return null;
}
});
}
}
}
//security/ex2/com/artima/security/stranger/Stranger.java
package com.artima.security.stranger;
import com.artima.security.doer.Doer;
import java.security.AccessController;
import java.security.PrivilegedAction;
public class Stranger implements Doer {
private Doer next;
private boolean direct;
public Stranger(Doer next,boolean direct) {
this.next = next;
this.direct = direct;
}
public void doYourThing() {
if (direct) {
next.doYourThing();
}
else {
AccessController.doPrivileged(new PrivilegedAction(){
public Object run() {
next.doYourThing();
return null;
}
});
}
}
可以通过检查的例子:(Example2a.java)
import com.artima.security.friend.Friend;
import com.artima.security.stranger.Stranger;
class Example2a {
public static void mani(String []args) {
TextFileDisplayer tfd = new TextFileDisplayer("question.txt");
Friend friend = new Friend(tfd,true);
Stranger stranger = new Stranger(friend,true);
stranger.doYourThing();
}
}
类 | 方法 | 保护域 | 栈 | |
Example2a | main() | CDROM | 1 | 栈底 |
com.artima.security.stranger.Stranger | doYourThing() | STRANGER | 2 | ↓ |
com.artima.security.friend.Friend | doYourThing() | FRIEND | 3 | ↓ |
TextFileDisplayer | doYourThing() | CDROM | 4 | ↓ |
java.io.FileReader | <init>() | BOOTSTRAP | 5 | ↓ |
java.i.FileInputStream | <init>() | BOOTSTRAP | 6 | ↓ |
java.lang.SecurityManager | checkRead() | BOOTSTRAP | 7 | ↓ |
java.lang.SecurityManager | checkPermission() | BOOTSTRAP | 8 | ↓ |
java.security.AccessController | checkPermission() | BOOTSTRAP | 9 | ↓ |
java.security.AccessControlContext | checkPermission() | BOOTSTRAP | 10 | 栈顶 |
表1-1中的保护域列表明每个栈帧都和下面四个保护域中的一个相关联:FRIEND、STRANGER、CD-ROM、以及的BOOTSTRAP。这些保护域中的三个和policyfile.txt策略文件中的grant子句相对应。其中和CD-ROM相关联的grant子句赋予由目录“${com.artima.ijvm.cdrom.home}/security/ex2"装载的代码读取文件question.txt和answer.txt文件的权限;BOOTSTRAP保护域代表赋予由启动类装载器装载的代码的任何权限,在保护域BOOTSTRAP中的代码被赋予了java.lang.AllPermission权限,该权限允许做任何事。
当AccessController执行它的栈检查时,它从栈帧10开始逐个向下直到栈帧1,栈帧1是由线程调用的第一个方法main方法,在Example2a中,调用栈的每一个栈帧都有权执行读取文件quesion.txt的操作,因此当AccessController执行checkPermission方法到达栈底时,不会抛出任何栈帧无权读取这个文件的情况,它将正常返回,并打印输出从question.txt读取的文件内容。
不能通过检查的例子:(Example2b.java)
import com.artima.security.friend.Friend;
import com.artima.security.stranger.Stranger;
class Example2a {
public static void main(String []args) {
TextFileDisplayer tfd = new TextFileDisplayer("answer.txt");
Friend friend = new Friend(tfd,true);
Stranger stranger = new Stranger(friend,true);
stranger.doYourThing();
}
}
<span style="font-size: 18px; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">Example2b.java与Example2a.java唯一不同之处在于Example2a将名字为question.txt的文件传递给TextFileDisplay构造器,而Example2b则是传递名字为answer.txt的文件,因为在栈中,有一个方法没有访问answer.txt文件的权限,因此该栈检查不能通过而抛出AccessControllerException异常。调用栈检查如表1-2所示:</span>
类 | 方法 | 保护域 | 栈 | |
Example2a | main() | CDROM | 1 | 栈底 |
com.artima.security.stranger.Stranger | doYourThing() | STRANGER | 2 | ← |
com.artima.security.friend.Friend | doYourThing() | FRIEND | 3 | ↓ |
TextFileDisplayer | doYourThing() | CDROM | 4 | ↓ |
java.io.FileReader | <init>() | BOOTSTRAP | 5 | ↓ |
java.i.FileInputStream | <init>() | BOOTSTRAP | 6 | ↓ |
java.lang.SecurityManager | checkRead() | BOOTSTRAP | 7 | ↓ |
java.lang.SecurityManager | checkPermission() | BOOTSTRAP | 8 | ↓ |
java.security.AccessController | checkPermission() | BOOTSTRAP | 9 | ↓ |
java.security.AccessControlContext | checkPermission() | BOOTSTRAP | 10 | 栈顶 |
使用doPrivileged() 方法
基本的AccessController算法防止了任何代码执行任何不可信任的代码,因此,一个权限较少的保护域的方法无权调用属于更高权限的保护域的方法,这个基本算法同时隐含了,如果一个属于较高权限保护域的方法调用了属于较低权限保护域的中的方法,它必须自动放弃某些权限。AccessController严格坚持在调用栈中的所有栈帧都必须含有执行被请求操作的权限,而这一点在很多情况下都太苛刻,为了使可信的代码执行不可靠的代码操作,AccessController类重载了四个名为doPrivileged( )静态方法,这四个方法都接收一个实现了java.security.PrivilegedAction接口或者java.security.PrivilegedExceptionAction接口的对象作为它的参数。当调用doPrivileged()方法时,就像调用其他任何方法一样,都会将一个新的栈帧压入栈。在由AccessController执行的栈检查中,一个doPrivileged()方法调用的栈帧标志了检查过程提前终止点。如果和调用doPr
ivileged()方法相关联的保护域拥有被请求操作权限,AccessController将立即返回,这样的操作就被允许,及时在栈下层的代码可能没有执行这个操作的权限。例如Example2c.java:
ivileged()方法相关联的保护域拥有被请求操作权限,AccessController将立即返回,这样的操作就被允许,及时在栈下层的代码可能没有执行这个操作的权限。例如Example2c.java:
import com.artima.security.friend.Friend;
import com.artima.security.stranger.Stranger;
class Example2a {
public static void mani(String []args) {
TextFileDisplayer tfd = new TextFileDisplayer("answer.txt");
Friend friend = new Friend(tfd,false);
Stranger stranger = new Stranger(friend,true);
stranger.doYourThing();
}
}
调用栈检查如表1-3所示:
表1-3 Example2c中的栈检查
在Example2c中要被检查的调用栈中加入了另外两个栈帧:栈帧4代表了doPrivileged()调用,栈帧5代表了PrivilegedAction对象的run()方法调用。当AccessController进行栈检查时,当它遇到栈帧4,发现是一个doPrivileged()调用,以此,AccessController又进行了一个检查:检查由栈帧3代表的代码,也就是调用了doPrivilegedd()的代码,是否有读取文件answer.txt的权限。因为栈帧3是和FRIEND保护域相关联的,而FRIEND保护域拥有读取文件answer.txt的权限,所以AccessController的checkPermission()方法正常返回,停止了栈帧3以后的栈帧检查。
执行(须将以上文件移到
d:\books\InsideJVM\manuscript\cdrom目录下):
java -Djava.security.manager -Djava.security.policy=policyfile.txt -Dcom.artima.ijvm.cdrom.home=d:\books\InsideJVM\manuscript\cdrom -cp .;jars/friend.jar;jars/stranger.jar Example2a