转自:http://www.importnew.com/17093.html,感谢分享

类加载器的作用就是将真实的class文件根据位置将该Java类的字节码装入内存,并生成对应的Class对象。用户可以通过继承ClassLoader和重写findClass方法来定义自己的类加载器进行加载,系统类加载器按照层次,分为:
(1).启动类加载器(Bootstrap ClassLoader):将加载 /JAVAHOME/lib以及为-Xbootclasspath所指定的目录下的类库,是核心Java API的class文件,用于启动Java虚拟机
(2).扩展类加载器(Extension ClassLoader):将加载/JAVAHOME/lib/ext以及为java.ext.dirs所指定的目录下的类库
(3).应用程序类加载器(Application/System ClassLoader):将加载ClassPath下所指定的类库,或者称为类路径加载器

1.双亲委派
    类的加载将使用双亲委派的方式,注意这里的双亲关系并非通过继承来实现,而是加载器之间指定或默认的委托加载关系,可以看到在 /java/lang/ClassLoader.java中,通过ClassLoader的构造方法显式指定了其父加载器,而若没有指定父加载器,那么将 会把系统类加载器AppClassLoader作为默认的父加载器

 private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
//...
} protected ClassLoader() {
//getSystemClassLoader()
this(checkCreateClassLoader(), getSystemClassLoader());
}

加载器对类的加载调用loadClass()方法实现:

 protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
// 该类没有被加载
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
// 先交由父加载器尝试加载
c = parent.loadClass(name, false);
} else {
// 父加载器为空,即为BootstrapClassLoader,那么查看启动类中是否有该类
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//父类无法加载该类,则由自己尝试加载
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name); // this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}

可见首先会检查该类是否已经被加载,若没有被加载,则会委托父加载器进行装载,只有当父加载器无法加载时,才会调用自身的findClass()方法进行加载。这样避免了子加载器加载一些试图冒名顶替可信任类的不可靠类,也不会让子加载器去实现父加载器实现的加载工作。
比如某个用户自定义的类加载器试图加载一个叫做“java.lang.String”的类,那么,该类会最终委派给启动类加载器 BootstrapClassLoader尝试加载,那么启动类加载器将加载Java API中的”java.lang.String”类而不会通过用户自定义的类加载器去获得和加载这个看上去不怀好意的冒名类。

但是仅仅依赖双亲委派是远远不够的,假设这个用户自定义的类加载器试图加载一个叫做“java.lang.Bomb”的危险类,而父类加载器无 法加载该类,那么加载工作将由用户定义的加载器负责实现。由于一个类的同一包内的类(和其子类)可以访问其protected成员,这个 “java.lang.Bomb”则可能访问可信任类的一些敏感信息,所以就必须将这个类与可信任类的访问域隔离,Java虚拟机只把这样彼此访问的特殊 权限授予由同一个类加载器加载的同一包内的类型,这样一个由同一个类加载器加载的、属于同一个包的多个类型集合称为运行时包。

2.命名空间
    类加载体系为不同类加载器加载的类提供不同的命名空间,同一命名空间内的类可以互相访问,不同命名空间的类不知道彼此的存在(除非显式提供访问机制)。同一类可以再不同的命名空间内,但无法在同一命名空间内重复出现。

命名空间是这样定义的:实际完成加载类的工作的加载器为定义类加载器,而加载的双亲委托路径上的所有加载器为初始类加载器,某个加载器的命名空间就是所有以该加载器为初始类加载器的类所组成。

可以预见,子加载器的命名空间包括其父/祖先加载器的命名空间和只有自己才可以加载的类所组成。根据加载体系结构的安全机制,同一命名空间内的类可 以互相访问,所以父加载器所加载的类不一定可以访问子加载器所加载的类,但子加载器所加载的类必然可以访问父加载器加载的类。父加载器加载的类就好像小箱 子,子加载器加载的类可能用到父加载器加载的类,就像一个大箱子,只能把小箱子放进大箱子,而不能反过来做(当然显式的访问机制除外)

以自己实现的类加载器为例:

 package com.ice.classloader;

 import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream; public class MyClassLoader extends ClassLoader { private String name; //加载器名称
private String path = "E:\\WorkSpace\\ClassLoaderTest\\"; //加载路径
private static final String HOME = "E:\\WorkSpace\\ClassLoaderTest\\";
private final String classFileType = ".class"; public MyClassLoader(String name) {
super();
this.name = name;
} public MyClassLoader(ClassLoader parent, String name) {
super(parent);
this.name = name;
} @Override
public String toString() {
return this.name;
} public String getPath() {
return path;
} public void setPath(String path) {
this.path = path;
} @Override
public Class<?> findClass(String name) throws ClassNotFoundException {
byte[] data = this.loadClassData(name);
if(data == null)
throw new ClassNotFoundException();
return this.defineClass(name, data, , data.length); } private byte[] loadClassData(String name) { InputStream is = null;
byte[] data = null;
ByteArrayOutputStream baos = null;
// System.out.println(" classloader:" + this.name + " try to load");
try {
//类名转化为路径
name = name.replace(".", "\\");
is = new FileInputStream(new File(path + name + classFileType)); baos = new ByteArrayOutputStream();
int ch = ;
while (- != (ch = is.read())) { baos.write(ch);
} data = baos.toByteArray();
}
catch (FileNotFoundException e) {
// e.printStackTrace();
return null;
}
catch (IOException ioe) {
ioe.printStackTrace();
}
finally {
try {
is.close();
baos.close();
}
catch (Exception e2) {
}
}
return data;
} public static void main(String[] args) throws Exception {
//假定的系统加载器
MyClassLoader father = new MyClassLoader("father");
father.setPath(HOME + "syslib\\"); MyClassLoader child = new MyClassLoader(father, "child");
child.setPath(HOME + "ext\\"); MyClassLoader user = new MyClassLoader("user");
user.setPath(HOME + "usr\\");
System.out.println("-------------test parent--------------");
//测试父加载器关系
traverseParent(child);
System.out.println("-------------test load begin from child--------------");
//测试加载
test(child);
//测试命名空间
System.out.println("-------------test namespace--------------");
testNameSpace(user); } public static void traverseParent(ClassLoader loader) throws Exception{
if(loader == null) return;
System.out.println("travase classloader:" + loader.toString());
while(loader.getParent() != null){
System.out.println(loader.getParent());
loader = loader.getParent();
}
} public static void test(ClassLoader loader) throws Exception {
Class<?> clazz = loader.loadClass("com.ice.classloader.LoadedClass");
Object object = clazz.newInstance();
} public static void testNameSpace(ClassLoader loader) throws Exception {
Class<?> clazz = loader.loadClass("com.ice.classloader.LoadedClass");
Object object = clazz.newInstance();
try{
LoadedClass lc = (LoadedClass) object;
}catch(Exception e){
e.printStackTrace();
}
}
}

被加载类LoadedClass的定义如下:

 //被加载类
package com.ice.classloader; public class LoadedClass { public LoadedClass() {
System.out.println("LoadedClass is loaded by: "
+ this.getClass().getClassLoader()); } }

(1).双亲委派结果
     child加载器会委托father进行加载,若father的加载目录下存在着对应的class文件,则会由父加载器father进行对应的加载工作(father也会交由AppClassLoader和ExtClassLoader尝试进行加载,但这两个加载器并不知道如何加载,故而最后会自己尝试进行加载)

当father的加载目录下没有对应的class文件,则会交由child进行加载

(2).命名空间隔离
    由于MyClassLoader是通过系统的(应用程序类加载器/类路径加载器加载的),而 LoadedClass是由user加载器所加载的,AppClassLoader加载器是user加载器的父加载器,故由父加载器加载的类 MyClassLoader无法看见子加载器user所加载的LoadedClass类,在MyClassLoader中尝试实例化 LoadedClass类时就会出现如下错误:

对应出错的正是尝试实例化LoadedClass类的那一行

try{
LoadedClass lc = (LoadedClass) object;
}catch(Exception e){

(3).运行时包
     当请求加载一个com.ice.classloader.virus类时,AppClassLoader路径下没有 该类的class文件,那么attaker加载器将会加载这个virus类,并暗示其为com.ice.classloader的一部分,该类想要获取 com.ice.classloader包下被信任类的访问权限。但由于权限检查时,由于该Virus类由attacker加载而非 AppClassLoader加载,故对MyClassLoader受保护成员的访问将会被阻止。

 package com.ice.classloader;
public class Virus { public Virus() {
System.out.println("Virus is loaded by: "
+ this.getClass().getClassLoader());
MyClassLoader cl = (MyClassLoader) this.getClass().getClassLoader();
System.out.println("secret is:" + cl.secret);
} }

MyClassLoader 由AppClassLoader所加载,而Virus由用户自定义的加载器attacker所加载,虽然AppClassLoader是attacker 的父加载器,即MyClassLoader对Virus可见,但由于两者不是由同一个加载器所加载,即不属于同一个运行时包,那么Virus对 MyClassLoader的受保护成员访问受限

 public class MyClassLoader extends ClassLoader {
protected int secret = -;
//...
public static void main(String[] args) throws Exception {
//其父加载器为Bootstrap ClassLoader
MyClassLoader loader = new MyClassLoader(null, "loader");
loader.setPath(HOME + "usr\\"); MyClassLoader attacker = new MyClassLoader("attacker");
attacker.setPath(HOME + "attacker\\"); System.out.println("MyClassLoader's classloader:" + MyClassLoader.class.getClassLoader()); System.out.println("-------------test parent--------------");
//测试父加载器关系
traverseParent(attacker); System.out.println("-------------test in-package access--------------");
testVirus(attacker); } public static void traverseParent(ClassLoader loader) throws Exception{
if(loader == null) return;
System.out.println("travase classloader:" + loader.toString());
while(loader.getParent() != null){
System.out.println(loader.getParent());
loader = loader.getParent();
}
} public static void testVirus(ClassLoader loader) throws Exception {
Class<?> clazz = loader.loadClass("com.ice.classloader.Virus");
Object object = clazz.newInstance();
}
}

结果如下:

注意命名空间的隔离与运行时包隔离的区别,不同命名空间的类之间不可见,而同一命名空间内的类可能由不同的加载器进行加载,如启动类加载器加载 的核心JavaAPI和用户自定义加载器加载的类,这些类及时声明定义为同一个包,但是由于不是由同一个加载器加载的,即不属于同一个运行时包,那么不同 运行时包内的类之间就存在对包可见成员的访问限制。

3.策略与保护域
    除了命名空间的访问隔离和双亲委派的受信类保护,类加载器体系还是用保护域来定义代码在运行时可以获得的权限。同样在分析保护域之前,先了解类Java虚拟机的安全访问控制及策略。

Java的沙箱模型可以由用户自定义,这是通过用户定制沙箱的安全管理器(SecurityManager)来定义沙箱的安全边界,以为程序运 行指定用户自定义的安全策略和访问控制。应用程序通过 System.setSecurityManager()/“-Djava.security.manager”来指定/启动安全管理器,每当 JavaAPI执行一些可能不安全的操作时,如对文件的读写和删除等,就会向安全管理器进行权限检查,若权限检查不通过,将会抛出一个安全异常,若权限检 查通过,则允许该操作的执行。

比如创建一个FileInputStream时,会调用SecurityManager的checkRead()进行读取权限的检查:

 public FileInputStream(File file) throws FileNotFoundException {
String name = (file != null ? file.getPath() : null);
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(name);
}
if (name == null) {
throw new NullPointerException();
}
fd = new FileDescriptor();
fd.incrementAndGetUseCount();
open(name);
}

checkRead()即以读动作的FilePermission为参数调用checkPermission()

 public void checkRead(String file) {
checkPermission(new FilePermission(file,
SecurityConstants.FILE_READ_ACTION));
}

jdk1.2版本后,可以使用checkPermission(Permission perm)和checkPermission(Permission perm, Object context)来进行权限检查,其中perm为请求执行操作所需要的权限,如java.io.FilePermission对“/usr /indata.txt”请求“read”操作。checkPermission()实际上在对当前线程的方法栈进行优化后,获得一个访问控制环境 AccessControlContext,并调用其checkPermission()方法.

public static void checkPermission(Permission perm)
throws AccessControlException
{
//System.err.println("checkPermission "+perm);
//Thread.currentThread().dumpStack(); if (perm == null) {
throw new NullPointerException("permission can't be null");
} AccessControlContext stack = getStackAccessControlContext();
// if context is null, we had privileged system code on the stack.
if (stack == null) {
//...debug相关
return;
} AccessControlContext acc = stack.optimize();
acc.checkPermission(perm);
}

checkPermission()会从方法的栈顶向栈底遍历(检查方法所在类的保护域权限,context是一个 ProtectionDomain数组),当遇到一个没有权限的栈帧就会抛出一个AccessControlException。即对于一次需要进行权限 检查的访问,对于该访问的方法的每一个调用层次都必须具有对应的访问权限。
对权限的判定是通过implies()来进行的,implies()在Permission类、PermissionCollection、ProtectionDomain类中声明。在Permission类(具体实现的子类)中,该方法将确定由该Permission所代表的对象,是否隐含了将要判断的Permission对象的权限中, 如对”/test/*”目录的读写权限testAllPermission,隐含了对”/test/test.txt”文件的读写权限 testFilePermission,即testAllPermission.implies(testFilePermission) 的值为true,反之为false。 在ProtectionDomain(其PermissionCollection)中,将进行权限集合内 implies()的判定,实际上就是在PermissionCollection中遍历保护域所拥有的权限,调用implies()判定其是否具有对应的访问权限。

 public void checkPermission(Permission perm)
throws AccessControlException
{
//...
if (context == null)
return; for (int i=; i< context.length; i++) {
if (context[i] != null && !context[i].implies(perm)) {
//...
throw new AccessControlException("access denied "+perm, perm);
}
} // ... return;
}

那么,类的访问权限(保护域)是如何指定的?
(1).类与访问权限是什么?
    每个class文件均和一个代码来源相关联,这个代码来源(java.security.CodeSource)通过URL类成员location指向代码库和对该class文件进行签名的零个或多个证书对象的数组(class文件在进行代码认证的过程中可能经过多个证书签名,也可能没有进行签名) 。
访问控制策略Policy对权限的授予是以CodeSource为基础进行的,每个CodeSource拥有若干个Permission,这些Permission对象会被具体地以其子类,如FilePermission、SocketPermission等描述,并且和CodeSource相关联的Permission对象将被封装在java.security.PermissionCollection(抽象类)的一个子类实例中,以描述该CodeSource所获取的权限。
(2).从类的加载到保护域探寻类访问权限的指定:
加载器会调用defineClass解析和加载类的Class实例:

 protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
{
protectionDomain = preDefineClass(name, protectionDomain); Class c = null;
String source = defineClassSourceLocation(protectionDomain); try {
c = defineClass1(name, b, off, len, protectionDomain, source);
} catch (ClassFormatError cfe) {
c = defineTransformedClass(name, b, off, len, protectionDomain, cfe,
source);
} postDefineClass(c, protectionDomain);
return c;
}

在defineClass()中,会调用preDefineClass()获取ProtectionDomain:

 private ProtectionDomain preDefineClass(String name,
ProtectionDomain pd)
{
if (!checkName(name))
throw new NoClassDefFoundError("IllegalName: " + name); if ((name != null) && name.startsWith("java.")) {
throw new SecurityException
("Prohibited package name: " +
name.substring(, name.lastIndexOf('.')));
}
if (pd == null) {
pd = defaultDomain;
} if (name != null) checkCerts(name, pd.getCodeSource()); return pd;
}

当没有指定保护域时,就会为其指定一个空的保护域,若指定了保护域则使用加载器所指定的保护域,类加载器的实现可以通过将代码来源(CodeSource),即代码库和该class文件的所有签名者信息,传递给当前的 Policy对象的getPermissions()方法,来查询该代码来源所拥有的权限集合PermissionCollection(在策略初始化时 生成),并以此构造一个保护域传递给defineClass(),以此指定类的保护域。

(3).Java应用程序访问控制策略是由抽象类java.security.Policy的子类实例所描述的,通过设置 policy.provider属性值来指定Policy的实现类,该属性值定义在/jre/lib/security/java.security文件 中

 #
# Class to instantiate as the system Policy. This is the name of the class
# that will be used as the Policy object.
#
policy.provider=sun.security.provider.PolicyFile

可见默认是使用PolicyFile类来实现访问控制策略,该类将使用从策略文件中读取并解析访问控制策略的方式形成策略,也可以通过实现自己的Policy并调用Policy的setPolicy()方法来替换当前Policy对象,对Java应用程序的访问控制策略是由抽象类java.security.Policy的子类实例实现的,其实现方式可以采用很多种方法,如从一个 结构化ASCII文件中读取,从一个Policy的二进制class文件中读取,从一个数据库中读取,PolicyFile就是使用了从ASCII策略文 件中读取的方法,策略文件定义在/jre/lib/security/java.security中:

 # The default is to have a single system-wide policy file,
# and a policy file in the user's home directory.
policy.url.=file:${java.home}/lib/security/java.policy
policy.url.=file:${user.home}/.java.policy

可以在java.security文件中修改或添加policy.url.x来指定用户自己想要的策略,也可以在运行时使用”-Djava.security.policy”命令行参数进行设置,如:

-Djava.security.manager -Djava.security.policy = mypolicy.txt
其中如果没有指定java.security.manager,那么应用程序就不会安装任何的安全管理器,而代码也就没有任何权限限制。mypolicy.txt就是用户自顶一个策略文件,这里使用的是相对路径,将使用程序的启动目录

以/jre/lib/security/java.policy为例说明策略文件,在该文件中使用上下文无关文法描述安全策略
如:

 grant codeBase "file:${{java.ext.dirs}}/*" {
permission java.security.AllPermission;
};

policy文件的基本语法如下:

 keystore "keystore_url",
"keystore_type"; grant [SignedBy "signer_names"] [, CodeBase "URL"] [,principal principal_class_name "principal_name",]{
Permission permission_class_name
[ "target_name" ]
[, "action"] [, SignedBy "signer_names"];
Permission ...
};
  • keystore:
    keystore_url指定了签名者的公钥的证书文件所在位置,可以使相对URL,如keystore “mykey”,这个相对路径指向了程序使用该策略文件的启动目录,比如,该策略文件由policy.url.x指定在”e://security /policy/mypolicy.txt”,那么公钥证书就在”e://security/policy/mykey”文件中。当然也可以使用绝对路径 指定公钥路径。
    keystore_type指定了密钥仓库信息的存储和数据格式,也定义了保护密钥仓库中私钥和密钥仓库完整性的算法,默认将使”JKS”类型
  • grant子句:
    授予 指定类型(代码) 指定权限
    其中对代码类型的描述有两种:
    signedBy表示签名者别名,可以是由”,”分隔的若干个签名者
    codeBase表示一个特定的加载位置,从该目录下加载的代码都将被赋予特定的权限
  • permission:
    permission由权限类型、操作目标、操作动作三部分组成,如
    permission java.io.FilePermission “note.txt” “read”即为对程序启动目录下(相对路径)的note.txt的读取权限

最后以深入jvm(第二版)一书中的例子来介绍策略文件的使用以及保护域的作用:

Doer接口:

 // /com/ice/security/doer/Doer.java
package com.ice.security.doer; public interface Doer {
void doYourThing();
}

Doer的实现类Friend,由Friend所签名,将作为受信认类访问“friend.txt”和“stranger.txt”

 // /com/ice/security/friend/Friend.java
package com.ice.security.friend; import java.security.AccessController;
import java.security.PrivilegedAction; import com.ice.security.doer.Doer; public class Friend implements Doer{
private Doer next;
private boolean direct; public Friend(Doer next, boolean direct){
this.next = next;
this.direct = direct;
} @Override
public void doYourThing() {
if(direct){
next.doYourThing();
}else{
AccessController.doPrivileged(
new PrivilegedAction() {
public Object run(){
next.doYourThing();
return null;
}
}
);
}
} }

Doer的实现类Stranger,由Stranger所签名,作为不受信认类,仅能访问“stranger.txt”

 //com/ice/security/stranger/Stranger.java
package com.ice.security.stranger; import java.security.AccessController;
import java.security.PrivilegedAction; import com.ice.security.doer.Doer; public class Stranger implements Doer{
private Doer next;
private boolean direct; public Stranger(Doer next, boolean direct){
this.next = next;
this.direct = direct;
} @Override
public void doYourThing() {
if(direct){
next.doYourThing();
}else{
AccessController.doPrivileged(
new PrivilegedAction() {
public Object run(){
next.doYourThing();
return null;
}
}
);
}
} }

txt文件的显示输出类TextFileDisplayer:

 //TextFileDisplayer.java
import java.io.CharArrayWriter;
import java.io.FileReader;
import java.io.IOException; import com.ice.security.doer.Doer; public class TextFileDisplayer implements Doer{
private String fileName;
public TextFileDisplayer(String fileName){
this.fileName = fileName;
} @Override
public void doYourThing() {
try{
FileReader fr = new FileReader(fileName);
try {
CharArrayWriter caw = new CharArrayWriter();
int c;
while((c = fr.read()) != -){
caw.write(c);
}
System.out.println(caw.toString());
} catch (IOException e) { }finally{
try{
fr.close();
}catch (IOException e){ }
}
}catch (IOException e) { }
} }

1.将Friend和Stranger分别导出为jar文件,放在指定目录(这里放在”E:\java\security”目录下)以待不同的机构 进行签名,Friend所在包假定为比较有信用的机构”friend”进行签名,而Stranger所在包假定为一个不受信任的机构”stranger” 进行签名。
(1).调用命令 jar cvf xxx.jar <_ClassPath> 进行打包
(注意打包后,若没有把jar包放在单独的目录下,需要删除原java文件编译产生的class文件,以免程序运行直接加载目录下class文件而非包内的class文件)
这里分别调用
jar cvf friend.jar com/ice/security/friend/*.class 将friend包内的class文件打包

jar cvf stranger.jar com/ice/security/stranger/*.class 将friend包内的class文件打包

(2).使用keytool可以用来生成新的密钥对,并与一个别名关联,用密码加以保护存放在keystore文件中
使用keytool -genkey -alias friend -keypass 123456 -validity 10000 -keystore mykey 命令:

该密钥的别名是friend,别名密码是123456(至少6位),有效期是10000天,存放在一个mykey的keystore文件中,keystore密码为myfriendkey

类似地,生成一个别名stranger的密钥对

为了方便起见,两个不同的签名者stranger和friend的密钥均存放在mykey中,mykey的访问密码是myfriendkey,密钥的访问密码都是123456

可以看到在目录下生成了一个mykey文件
(3).使用jarsigner -keystore -storepass -keypass 命令进行签名
这里:
jarsigner -keystore mykey -storepass myfriendkey -keypass 123456 friend.jar friend
jarsigner -keystore mykey -storepass myfriendkey -keypass 123456 stranger.jar stranger
使用friend密钥对friend.jar进行签名,使用stranger密钥对stranger.jar进行签名

(4).最后可以使用
keytool -export -alias -storepass -file -keystore
这里分别用:
keytool -export -alias friend -storepass myfriendkey -file friend.cer -keystore mykey
keytool -export -alias stranger -storepass myfriendkey -file stranger.cer -keystore mykey
导出friend和stranger的发行证书

2.编写自己的策略文件,放在当前目录下

 //mypolicy.txt
keystore "mykey"; grant signedBy "friend"{
permission java.io.FilePermission "friend.txt","read";
permission java.io.FilePermission "stranger.txt","read";
}; grant signedBy "stranger"{
permission java.io.FilePermission "stranger.txt","read";
}; grant codeBase "file:${com.ice.home}/com*"{
permission java.io.FilePermission "friend.txt","read";
permission java.io.FilePermission "stranger.txt","read";
};

这里friend签名的类和${com.ice.home}.com(后面设置 为”e:\java\security\com”,存放着Doer接口的class文件)可以读取”friend.txt” 和”stranger.txt”,而stranger签名的类只能读取”stranger.txt”
(这里为了方便,直接使用mykey而非发布的证书)
(1).添加Doer接口类的class文件(对应路径)和friend.txt和stranger.txt两个测试文件
(2).通过权限检查的例子:

 public class ProtectionDomainTest {
public static void main(String[] args){
TextFileDisplayer tfd = new TextFileDisplayer("stranger.txt");
Friend friend = new Friend(tfd, true);
Stranger stranger = new Stranger(friend, true);
stranger.doYourThing();
}
}

调用java -Djava.security.manager -Djava.security.policy=mypolicy.txt -Dcom.ice.home=e:\java\security -cp .;friend.jar;stranger.jar ProtectionDomainTest测试运行,其中指定了com.ice.home的路径,通过-cp设置了类路径

(3).不能通过权限检查的例子:

 public class ProtectionDomainTest {
public static void main(String[] args){
TextFileDisplayer tfd = new TextFileDisplayer("friend.txt");
Friend friend = new Friend(tfd, true);
Stranger stranger = new Stranger(friend, true);
stranger.doYourThing();
}
}

与(2)类似,但stranger会尝试让friend读取”friend.txt”,这会被阻止

JVM学习之实例分析JVM安全体系的更多相关文章

  1. jvm学习(2)JVM内存说明

    前言 一.类方法 类方法是静态方法,前面需要有static修饰符修饰.类方法内不能涉及有关变量的内容1.不能调用类的对象方法2.不能引用对象变量3.类方法不能被重写(覆盖)4.类方法不能使用super ...

  2. 实例分析JVM安全体系:双亲委派、命名空间、保护域、策略

    在了解双亲委派模型之前,先了解一下类加载器的概念: 类加载器的作用就是将真实的class文件根据位置将该Java类的字节码装入内存,并生成对应的Class对象.用户可以通过继承ClassLoader和 ...

  3. 【JVM学习笔记一】JVM内存分布

    Overview 学习JVM首先需要了解一下JVM管理的内存是如何分布的,在看了<深入理解Java虚拟机>和一些博文之后,我准备自己记录一下学习的过程. 下图是JVM中运行时数据区的大致示 ...

  4. java jvm学习笔记十三(jvm基本结构)

    欢迎装载请说明出处:http://blog.csdn.net/yfqnihao 这一节,主要来学习jvm的基本结构,也就是概述.说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完 ...

  5. JVM学习八:常用JVM配置参数

    前面学习的都是和类加载相关的知识,接下来学习的则和GC相关的知识,都是JVM的几个重点块. 零.在IDE的后台打印GC日志: 既然学习JVM,阅读GC日志是处理Java虚拟机内存问题的基础技能,它只是 ...

  6. JVM学习十二:JVM之性能监控工具

    前面我们学习了很多JVM相关的理论知识,那么本节将重点讲述的是工具的使用,正所谓:工欲善其事,必先利其器.因此,本节介绍常用的性能监控工具,用于性能监控和问题排查. 一.系统性能监控 系统性能工具用于 ...

  7. JVM学习第三天(JVM的执行子系统)之类加载机制

    好几天没有学习了,前几天因为导出的事情,一直在忙,今天继续学习, 其实今天我也遇到了一个问题,如果有会的兄弟可以评论留给我谢谢; 问题:fastJSON中JSONObject.parseObject做 ...

  8. JVM学习第三天(JVM的执行子系统)之字节码指令

    早上看了Class类文件结构,晚上继续来看字节码指令,毕竟谁也不是一步登天的(说白了还是穷); 字节码指令 Java虚拟机的指令由一个字节长度的.代表着某种特定操作含义的数字(称为操作码,Opcode ...

  9. JVM学习第三天(JVM的执行子系统)之开篇Class类文件结构

    虽然这几天 很忙,但是学习是不能落下的,也不能推迟,因为如果推迟了一次,那么就会有无数次;加油,come on! Java跨平台的基础: 各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节 ...

随机推荐

  1. Android studio SweetAlert for Android

    找到个开源项目.github:https://github.com/pedant/sweet-alert-dialog 效果图:https://raw.githubusercontent.com/pe ...

  2. 一致性hash

    1,一致性hash函数选择 crc32(范围为0到2的32次方),超过最大值,需要求模 :md5,求得16进制数据,超过最大值,需要求模 : 2,对cache server的虚拟节点的某些唯一属性或者 ...

  3. Mysql Binlog Dump原理

    Register 首先,我们需要伪造一个slave,向master注册,这样master才会发送binlog event.注册很简单,就是向master发送COM_REGISTER_SLAVE命令,带 ...

  4. 检索n以内所有素数

    #include <iostream>#include <cmath> const int N=500000;//数组大小 using namespace std; int m ...

  5. EC读书笔记系列之20:条款53、54、55

    条款53 不要轻忽编译器的警告 记住: ★严肃对待编译器发出的警告信息.努力在你的编译器的最高(最严苛)警告级别下争取“无任何警告”的荣誉 ★不要过度依赖编译器的报警能力,∵不同的编译器对待事情的态度 ...

  6. AfxEnableControlContainer()

    1)OLE(Object Linking and Embedding,对象连接与嵌入).是一种面向对象的技术,利用OLE可开发可重复使用的软件组件(COM). 2)ActiveX 控件是基于组件对象模 ...

  7. 使用file_get_content系列函数和使用curl系列函数采集图片的性能对比

    由于公司的一个汽车网站的后台的汽车内容都是主要是来自与汽车之家的,编辑的同事们必须天天手动去对着汽车之家来添加汽车,实在是太蛋疼了.于是乎,为了改变这种状况,作为一个开发码农,我的任务就来了...那就 ...

  8. USB OTG学习

    1. 概要 OTG设备使用插头中的ID引脚来区分A/B Device,ID接地被称作为A-Device,为连接时候的USB Host,A-Device始终为总线提供电力,ID悬空被称作为B-Devic ...

  9. 论山寨手机与Android联姻 【1】MTK亮相的历史背景

    [1]MTK亮相的历史背景如果说1960年代是大型机(Mainframe)的时代,1970年代是小型机(Microcomputer)的时代,那么1980年代无疑是个人电脑(PC)的时代,而1990年代 ...

  10. firemonkey打开子窗体(匿名回调函数)

    procedure TForm1.Button1Click(Sender: TObject);varChildForm: TForm2;beginChildForm := TForm2.Create( ...