策略
Java安全体系结构的真正好处在于,它可以对代码授予不同层次的信任度来部分地访问系统。
Microsoft提供了ActiveX控件认证技术,它和Java的认证技术相类似,但是ActiveX控件并不在沙箱中运行。这样,使用了ActiveX,一系列移动代码要么是被完全信任的,要么是完全不被信任的。
版本1.2的安全体系结构的主要目标之一就是使建立(以签名代码为基础的)细粒度的访问控制策略的过程更为简单且更少出错。
在版本1.2的安全体系结构中,对应于整个Java应用程序的一个访问控制策略是由抽象类java.security.Policy的一个子类的单个实例所表示的。
安全策略是一个从描述运行代码的属性集合到这段代码所拥有的权限的映射。在版本1.2的安全体系结构中,描述运行代码的属性被总称为代码来源。一个代码来源是由一个java.security.CodeSource对象表示的,这个对象中包含了一个java.net.URL,它表示代码库和代表了签名者的零个或多个证书对象的数组。证书对象是抽象类java.security.cert.Certificate的子类的一个实例,一个Certificate对象抽象表示了从一个人到一个公钥的绑定,以及另一个为这个绑定作担保的人(以前提过的证书机构)。CodeSource对象包含了一个Certificate对象的数组,因为同一段代码可以被多个团体签名(担保)。这个签名通常是从JAR文件中获得的。
权限是用抽象类java.security.Permission的一个子类的实例表示的。一个Permission对象有三个属性:类型、名字和可选的操作。
在Policy对象中,每一个CodeSource是和一个或多个Permission对象相关联的。和一个CodeSource相关联的Permission对象被封装在java.security.PermissionCollection的一个子类实例中。
策略文件
java.security.Policy是一个抽象类,具体Policy子类的实现细节之一就是该子类的实例怎样知道策略应该是什么。子类可以采取多种方法,例如对一个已序列化的Policy对象进行并行化,从数据库中抽取策略,或者从文件中读取策略。由Sun提供的在Java 1.2平台下的具体Policy子类采用了最后一种方法:在一个ASCII策略文件中用上下文无关方法描述安全策略。
一个策略文件包括了一系列grant子句,每一个grant子句将一些权限授给一个代码来源。
策略文件例子policyfile.txt:
keystore "ijvmkeys"; grant signedBy "friend" { permission java.io.FilePermission "question.txt", "read"; permission java.io.FilePermission "answer.txt", "read"; }; grant signedBy "stranger" { permission java.io.FilePermission "question.txt", "read"; }; grant codeBase "file:${com.artima.ijvm.cdrom.home}/security/ex2/*" { permission java.io.FilePermission "question.txt", "read"; permission java.io.FilePermission "answer.txt", "read"; };
说明:
keystore "ijvmkeys"子句说明密钥别名存储在名为ijvmkeys的keystore文件中。
第1个grant子句将授予由别名为“friend”的实体签名的所有代码两个权限。被授予的权限是:读取question.txt、answer.txt文件的权限。因为这个grant子句中没有提到代码库,所以由friend签名的代码可以来自任何代码库。
第2个grant子句只授予了读取question.txt文件的权限。
第3个grant子句将两个权限授予所有从一个特定目录中装载的代码。这个grant子句没有指明任何签名者,所以,这个代码可以是被任何人签名的,或者是未被签名的。
保护域
当类装载器将类型装入Java虚拟机时,它们将为每个类型指派一个保护域。保护域定义了授予一段特定代码的所有权限。(一个保护域对应策略文件中的一个或多个grant子句。)装载入Java虚拟机的每一个类型都属于一个且仅属于一个保护域。
访问控制器
类java.security.AccessController提供了一个默认的安全策略执行机制,它使用栈检查来决定潜在不安全的操作是否被允许。这个访问控制器不能被实例化,它不是一个对象,而是集合在单个类中的多个静态方法。AccessController的最核心方法是它的静态方法checkPermission(),这个方法决定一个特定的操作能否被允许。
如果你安装了具体安全管理器,其实最终是由这个AccessController来决定一个潜在不安全的方法是否否被允许。
每一个栈帧代表了由当前线程调用的某个方法,每一个方法是在某个类中定义的,每一个类又属于某个保护域,每个保护域包含一些权限。因此,每个栈帧间接地和一些权限相关。
1)implies()方法
为了决定由传递给AccessController的checkPermission()方法的Permission对象所代表的操作,是否包含在(或隐含在)和调用栈中的代码相关联的权限中,AccessController利用了一个名为implies()的重要方法。这个implies()方法是在Permission类以及PermissionCollection类和ProtectionDomain类中声明的。
import java.security.Permission; import java.io.FilePermission; import java.io.File; // On CD-ROM in file security/ex1/Example1.java class Example1 { public static void main(String[] args) { char sep = File.separatorChar; // Read permission for "/tmp/f" Permission file = new FilePermission(sep + "tmp" + sep + "f", "read"); // Read permission for "/tmp/*", which // means all files in the /tmp directory // (but not any files in subdirectories // of /tmp) Permission star = new FilePermission(sep + "tmp" + sep + "*", "read"); boolean starImpliesFile = star.implies(file); boolean fileImpliesStar = file.implies(star); // Prints "Star implies file = true" System.out.println("Star implies file = " + starImpliesFile); // Prints "File implies star = false" System.out.println("File implies star = " + fileImpliesStar); } }
2)栈检查示例
下面几节将给出几个示例,说明AccessController执行栈检查的方法。
基于深入理解Java虚拟机读书笔记之:第3章 安全(2)的代码示例,增加一个类:
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.write(c); } System.out.println(caw.toString()); } catch (IOException e) { } finally { try { fr.close(); } catch (IOException e) { } } } catch (IOException e) { } } }
这个类的doYourThing()方法作用是显示一个文本文件的内容。
3)一个回答“是”的栈检查
在第一个栈检查示例中,先来看一个Example2a应用程序:
import com.artima.security.friend.Friend; import com.artima.security.stranger.Stranger; // This succeeds because everyone has permission to // read answer.txt class Example2a { public static void main(String[] args) { TextFileDisplayer tfd = new TextFileDisplayer("question.txt"); Friend friend = new Friend(tfd, true); Stranger stranger = new Stranger(friend, true); stranger.doYourThing(); } }
当TextFileDisplayer的doYourThing()方法创建了一个新的FileReader对象时,FileReader的构造器创建了一个新的FileInputStream,FileInputStream的构造器检查是否已经安装了一个安全管理器。在现在这个例子中,已经安装了具体安全管理器,因此,FileInputStream的构造器调用了具体安全管理器的checkRead()方法。这个checkRead()方法实例化了一个新的、代表读文件question.txt权限的FilePermission对象,并将这个对象传给具体安全管理器的checkPermission()方法,这个checkPermission()方法又把这个对象传递给AccessController的checkPermission()方法。AccessController()的checkPermission()方法执行了栈检查,确定这个线程是否有权打开并读取文件question.txt。
Example2a中的栈检查:所有栈帧都有权限
调用栈的每一个栈帧用由多个元素组成的一行表示。保护域表示了这个栈帧所关联的保护域。最右边的箭头,说明了当一个AccessController的checkPermission()方法检查每一个栈帧是否有权执行被请求的操作时,它的行进方向。在箭头的左边是数字,每个栈帧对应一个。栈的顶显示在图的最底端。
FRIEND、STRANGER和CDROM三个保护域和policyfile.txt文件中的grant子句相对应,BOOTSTRAP保护域代表赋予所有由启动类装载器装载的代码的权限,在该保护域中的代码被赋予了java.lang.AllPermission,该权限允许做任何事。
运行示例代码(注:附件cdrom.zip的security/ex2目录下的ex2a.bat文件):
java -Djava.security.manager -Djava.security.policy=policyfile.txt -Dcom.artima.ijvm.cdrom.home=D:\cdrom -cp .;jars/friend.jar;jars/stranger.jar Example2a
运行结果:
Too what extent does complexity threaten security?
4)一个回答“不”的栈检查
下面一个栈检查的例子,是没有权限情况下的栈检查。
import com.artima.security.friend.Friend; import com.artima.security.stranger.Stranger; // This fails because the Stranger code doesn't have // permission to read file question.txt class Example2b { 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(); } }
本例子与上个例子的区别仅仅是将TextFileDisplayer的参数question.txt替换为answer.txt。
在这个例子中,检查过程并没有真正到达栈帧1,当AccessController到达栈帧2时,它发现栈帧2的doYourThing()方法属于Stranger类的代码,而这个类没有读取answer.txt文件的权限。AccessController的checkPermission()方法抛出了一个AccessControllerException异常。
Example2b中的栈检查:栈帧2没有权限
运行示例代码(注:附件cdrom.zip的security/ex2目录下的ex2b.bat文件):
java -Djava.security.manager -Djava.security.policy=policyfile.txt -Dcom.artima.ijvm.cdrom.home=D:\cdrom -cp .;jars/friend.jar;jars/stranger.jar Example2b
运行结果:
Exception in thread "main" java.security.AccessControlException: access denied (
java.io.FilePermission answer.txt read)
at java.security.AccessControlContext.checkPermission(Unknown Source)
at java.security.AccessController.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkRead(Unknown Source)
at java.io.FileInputStream.<init>(Unknown Source)
at java.io.FileInputStream.<init>(Unknown Source)
at java.io.FileReader.<init>(Unknown Source)
at TextFileDisplayer.doYourThing(TextFileDisplayer.java:19)
at com.artima.security.friend.Friend.doYourThing(Friend.java:45)
at com.artima.security.stranger.Stranger.doYourThing(Stranger.java:45)
at Example2b.main(Example2b.java:18)
5)doPrivileged()方法
有的时候,调用栈较上层(更靠近栈顶)的代码可能希望执行一段代码,而这段代码在调用栈的较下层是不允许执行的。
为了使可信的代码执行较不可靠的代码操作(这段不可靠的代码位于调用栈的较下层且没有执行这个操作的权限),AccessController类重载了四个名为doPrivileged()的静态方法。
当调用doPrivileged()方法时,就像调用其他任何方法一样,都会将一个新的栈帧压入栈。在由AccessController执行的栈检查中,一个doPrivileged()方法调用的栈帧标识了检查过程的提前终止点。如果和调用doPrivileged()的方法相关联的保护域拥有执行被请求操作的权限,AccessController将立即返回。这样这个操作就被允许,即使在栈下层的代码可能没有执行这个操作的权限。
看一看Friend的doPrivileged()的调用过程例子:
import com.artima.security.friend.Friend; import com.artima.security.stranger.Stranger; // This succeeds because Friend code executes a // doPrivileged() call. (Passing false as // the second arg to Friend constructor causes // it to do a doPrivileged().) class Example2c { public static void main(String[] args) { TextFileDisplayer tfd = new TextFileDisplayer("answer.txt"); Friend friend = new Friend(tfd, false); Stranger stranger = new Stranger(friend, true); stranger.doYourThing(); } }
Example2c中的栈检查:在栈帧3停止
与前两个例子的调用栈区别在于Example2c的调用栈有两个另外的栈帧:栈帧4代表了doPrivileged()调用,栈帧5代表了PrivilegedAction对象的run()调用。
当AccessController到达栈帧4时,它发现了一个doPrivileged()调用。因此,AccessController又进行了一个检查:它检查由栈帧3代表的代码,也就是调用了doPrivileged()的代码,是否有读取文件answer.txt的权限。因为栈帧3是与FRIEND保护域相关联的,而FRIEND保护域拥有读取文件question.txt的权限,所以AccessController的checkPermission()方法正常返回。
运行示例代码(注:附件cdrom.zip的security/ex2目录下的ex2c.bat文件):
java -Djava.security.manager -Djava.security.policy=policyfile.txt -Dcom.artima.ijvm.cdrom.home=D:\cdrom -cp .;jars/friend.jar;jars/stranger.jar Example2c
运行结果:
Complexity threatens security to a significant extent. The more
complicated a security infrastructure becomes, the more likely
parties responsible for configuring security will either make
mistakes that open up security holes or avoid using the
security infrastructure altogether.
5)doPrivileged()的一个无效使用
有一点很重要,必须理解,那就是一个方法不能授予它自己比它现在已经用doPrivileged()调用所得到的权限更多的权限。通过调用doPrivileged(),一个方法仅仅能使用它现在已经被授予的权限。
作为一个doPrivileged()的无效使用的例子:
import com.artima.security.friend.Friend; import com.artima.security.stranger.Stranger; // This fails because even though Stranger does // a doPrivileged() call, Stranger doesn't have // permission to read question.txt. (Passing // false as second arg to Stranger constructor // causes it to do a doPrivileged().) class Example2d { public static void main(String[] args) { TextFileDisplayer tfd = new TextFileDisplayer("answer.txt"); Stranger stranger = new Stranger(tfd, false); Friend friend = new Friend(stranger, true); friend.doYourThing(); } }
Example2d中的栈检查:栈帧5没有权限
当AccessController到达栈帧5,它发现这个栈帧和STRANGER保护域相关联,而STRANGER保护域没有读取文件answer.txt的权限。在这种情况下,AccessController抛出一个AccessControlException异常,说明请求读取answer.txt的操作不能被执行。
运行示例代码(注:附件cdrom.zip的security/ex2目录下的ex2d.bat文件):
java -Djava.security.manager -Djava.security.policy=policyfile.txt -Dcom.artima.ijvm.cdrom.home=D:\cdrom -cp .;jars/friend.jar;jars/stranger.jar Example2d
运行结果:
Exception in thread "main" java.security.AccessControlException: access denied (
java.io.FilePermission answer.txt read)
at java.security.AccessControlContext.checkPermission(Unknown Source)
at java.security.AccessController.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkRead(Unknown Source)
at java.io.FileInputStream.<init>(Unknown Source)
at java.io.FileInputStream.<init>(Unknown Source)
at java.io.FileReader.<init>(Unknown Source)
at TextFileDisplayer.doYourThing(TextFileDisplayer.java:19)
at com.artima.security.stranger.Stranger$1.run(Stranger.java:51)
at java.security.AccessController.doPrivileged(Native Method)
at com.artima.security.stranger.Stranger.doYourThing(Stranger.java:48)
at com.artima.security.friend.Friend.doYourThing(Friend.java:45)
at Example2d.main(Example2d.java:21)
在此说明,本系列文章的内容均出自《深入理解Java虚拟机》一书,除了极少数的“注”或对内容的裁剪整理外,内容原则上与原书保持一致。由于这是一本原理性的书籍,本人不想由于自己能力与理解的问题对大家造成误解,所以除了对原书内容的裁剪整理之外,不做任何内容的延伸思考与扩展。
另外,如果您对本系列文章的内容感兴趣,建议您去阅读原版书籍,谢谢!
(转载请注明来源:http://zhanjia.iteye.com/blog/1842733)
相关推荐
总的来说,"深入理解Java虚拟机读书笔记之:第3章 安全(2)"主要涵盖了Java安全体系的核心概念,包括类加载器、权限模型、安全管理器以及相关工具的使用。理解这些内容对于任何希望构建安全、可靠的Java应用程序的...
第三步,内存空间初始化;第四步,必要的设置。对象的内存布局在 HotSpot 虚拟机中,对象在内存中存储的布局分为三块:对象头、实例数据和对齐填充。对象头包括两部分信息, 一部分用于存储对象自身的运行时数据,如...
### 学习深入理解Java虚拟机的前几章笔记 #### JVM内存模型 Java虚拟机(JVM)的内存模型主要分为两大类:线程共享区和线程私有区。 ##### 线程共享区 - **堆**:是所有线程共享的内存区域,在这里存放着对象实例...
读书笔记:周志明老师《深入理解Java虚拟机》第三版阅读笔记
读书笔记:《深入理解Java虚拟机 Jvm高级特性与最佳实践第三版》阅读笔记
《JVM:深入理解Java虚拟机》是一本深入解析Java虚拟机工作原理和技术细节的经典书籍。这份学习笔记将涵盖JVM的关键概念、架构以及它如何影响Java程序的性能。我们将探讨以下几个方面: 1. **JVM概述** Java虚拟机...
Java虚拟机(JVM)是运行Java字节码的虚拟环境,它位于操作系统之上,硬件之下,提供了一层软件抽象,使得Java程序可以在多种平台上运行而无需重新编译。JVM的核心功能包括内存管理、垃圾收集、安全性和平台独立性。...
的第三部分。第四部分对应于原书的第四部分,程序编译与代码优化,不过仅对 Java 的运行期优化,也就是 JIT 时进行的优化进行了总结,编译器优化部分尚未进行深入研究。 阅读方法: 本 repo 的 README.md 从头读到尾...
《深入理解Java虚拟机》是Java开发者们深入探讨Java运行机制的经典之作,作者周志明以其深入浅出的讲解方式,揭示了Java虚拟机(JVM)的工作原理。本资源包含该书第三版的源码分析及学习笔记,旨在帮助读者更透彻地...
本repository为《深入理解Java虚拟机 - Jvm高级特性与最佳实践(第三版)》阅读笔记,因为第一章主要讲的是Java的发展历史,这里就不做笔记,直接从第2章的"Java内存区域与内存溢出异常"讲起。 第二部分 自动内存...
作为笔记的作者,被称为“专业第一”,意味着笔记内容会覆盖Java的基础语法、面向对象的深入理解和高级特性。 2. 面向对象编程:在Java中,一切皆为对象,这是Java最重要的编程范式之一。面向对象编程的基本概念...
6. **跨平台性**:Java的一个重要特性就是“编写一次,到处运行”(Write Once Run Anywhere, WORA),这得益于Java的运行机制——Java程序被编译成字节码(Bytecode),然后由Java虚拟机(JVM)解释执行。...
### 毕向东Java笔记知识点总结 #### 第一章:编程基础 1. **Java的特性和优势**: - **简单性**:Java的设计哲学是“使编程变得简单”。 - **面向对象**:Java完全支持面向对象编程概念如封装、继承和多态。 - ...
3. **运行程序**:通过Java虚拟机(`java.exe`)执行编译后的`.class`文件。 #### 注释 - **单行注释**:使用`//`标记。 - **多行注释**:使用`/* */`包裹注释内容。 #### Java编程方向 - **Java SE (J2SE)**:...
它的设计目标是“一次编写,到处运行”(Write Once, Run Anywhere, WORA),通过Java虚拟机(JVM)实现了这一目标,可以在不同操作系统上运行Java程序。 2. **与.NET框架的区别**: - 平台独立性:Java代码编译成...
深入理解java虚拟机:jvm高级特性与最佳实践源码现代操作系统,第 4 版 作者:Andrew S. Tanenbaum, Herbert Bos ** Notes by Henry Cooksley ** 1 介绍 操作系统、外壳、GUI、图形用户界面、内核模式、主管模式、...
2023年的"Java 基础 第1阶段:基本语法——尚硅谷学习笔记(含面试题)"涵盖了从环境搭建到程序设计的基本要素,旨在帮助学习者系统地理解并应用Java语言。 一、Java环境搭建 在开始Java编程之前,你需要安装Java ...
### Java入门第一季笔记 #### Java基本概念及发展概述 - **Java简介**:Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems公司于1995年发布,现归Oracle公司所有。Java的设计目标是高可靠性、安全性、...