通过AccessController看Java安全模型

作为一种诞生于互联网兴起时代的语言,Java 从一开始就带有安全上的考虑,如何保证通过互联网下载到本地的 Java 程序是安全的,如何对 Java 程序访问本地资源权限进行有限授权,这些安全角度的考虑一开始就影响到 Java 语言的设计与实现。可以说 Java 在这些方面的探索与经验,对后来的一些语言与产品都带来了积极影响。

Java 中的安全模型

在 Java 中将执行程序分成本地和远程两种,本地代码默认视为可信任的,而远程代码则被看作是不受信的。对于授信的本地代码,可以访问一切本地资源。而对于非授信的远程代码在早期的 Java 实现中,安全依赖于沙箱 (Sandbox) 机制。沙箱机制就是将 Java 代码限定在虚拟机 (JVM) 特定的运行范围中,并且严格限制代码对本地系统的资源访问,通过这样的措施来保证对远程代码的有效隔离,防止对本地系统造成破坏。如图 1 所示,

图 1.JDK1.0 安全模型

但如此严格的安全机制也给程序的功能扩展带来障碍,比如当用户希望远程代码访问本地系统的文件时候,就无法实现。因此在后续的 Java1.1 版本中,针对安全机制做了改进,增加了安全策略,允许用户指定代码对本地资源的访问权限。如图 2 所示,

图 2.JDK1.1 安全模型

在 Java1.2 版本中,再次改进了安全机制,增加了代码签名。不论本地代码或是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中权限不同的运行空间,来实现差异化的代码执行权限控制。如图 3 所示,

图 3.JDK1.2 安全模型

当前最新的安全机制实现,则引入了域 (Domain) 的概念。虚拟机会把所有代码加载到不同的系统域和应用域,系统域部分专门负责与关键资源进行交互,而各个应用域部分则通过系统域的部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域 (Protected Domain),对应不一样的权限 (Permission)。存在于不同域中的类文件就具有了当前域的全部权限,如图 4 所示:

图 4. 最新安全模型

以上提到的都是基本的 Java 安全模型概念,在应用开发中还有一些关于安全的复杂用法,其中最常用到的 API 就是 doPrivileged。doPrivileged 方法能够使一段受信任代码获得更大的权限,甚至比调用它的应用程序还要多,可做到临时访问更多的资源。有时候这是非常必要的,可以应付一些特殊的应用场景。例如,应用程序可能无法直接访问某些系统资源,但这样的应用程序必须得到这些资源才能够完成功能。针对这种情况,Java SDK 给域提供了 doPrivileged 方法,让程序突破当前域权限限制,临时扩大访问权限。下面内容会详细讲解一下安全相关的方法使用。

访问控制器

类java.security.AccessController提供了一个默认的安全策略执行机制,它使用栈检查来决定潜在不安全的操作是否被允许。

这个访问控制器不能被实例化,它不是一个对象,而是集合在单个类中的多个静态方法。

 如果你安装了具体安全管理器,其实最终是由这个AccessController来决定一个潜在不安全的方法是否否被允许。
     每一个栈帧代表了由当前线程调用的某个方法,每一个方法是在某个类中定义的,每一个类又属于某个保护域,每个保护域包含一些权限。

因此,每个栈帧间接地和一些权限相关。

AccessController 用于访问控制操作和决定。再详细一点, AccessController主要用于以下三个目的:

1.  根据当前有效的安全策略决定是否允许或拒绝对关键资源的访问。

2.  将代码标记为"特权",从而影响后续访问决定。

3.  获取当前调用上下文的“快照”,这样便可以相对于已保存的上下文作出其他上下文的访问控制决定。

AccessController的最核心方法是它的静态方法checkPermission(),这个方法决定一个特定的操作能否被允许。

FilePermission perm = new FilePermission("/temp/testFile", "read");
  AccessController.checkPermission(perm);
在这个例子中, checkPermission将决定是否授予"读"权限给文件testFile。
如果访问请求被允许, checkPermission 会安静的返回,如果拒绝, 将会抛出一个AccessControlException。

[java] view plain copy

  1. for (int i = m; i > 0; i--) {
  2. if (caller i‘s domain does not have the permission)
  3. throw AccessControlException
  4. else if (caller i is marked as privileged) {
  5. if (a context was specified in the call to doPrivileged)
  6. context.checkPermission(permission)
  7. if (limited permissions were specified in the call to doPrivileged) {
  8. for (each limited permission) {
  9. if (the limited permission implies the requested permission)
  10. return;
  11. }
  12. } else
  13. return;
  14. }
  15. }
  16. // Next, check the context inherited when the thread was created.
  17. // Whenever a new thread is created, the AccessControlContext at
  18. // that time is stored and associated with the new thread, as the
  19. // "inherited" context.
  20. inheritedContext.checkPermission(permission);

在某一个线程的调用栈中,当 AccessController 的 checkPermission 方法被最近的调用程序(例如 A 类中的方法)调用时,对于程序要求的所有访问权限,ACC 决定是否授权的基本算法如下:
1. 如果调用链中的某个调用程序没有所需的权限,将抛出 AccessControlException;
2. 若是满足以下情况即被授予权限:
a. 调用程序访问另一个有该权限域里程序的方法,并且此方法标记为有访问“特权”;
b. 调用程序所调用(直接或间接)的后续对象都有上述权限。

当然了,Java SDK 给域提供了 doPrivileged 方法,让程序突破当前域权限限制,临时扩大访问权限。

创建一个项目projectX:

[java] view plain copy

  1. package com.dusk;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.security.AccessControlException;
  5. import java.security.AccessController;
  6. import java.security.PrivilegedAction;
  7. public class FileUtil {
  8. // 工程 A 执行文件的路径
  9. private final static String FOLDER_PATH = "C:\\Users\\dushangkui\\workspace\\projectX\\bin";
  10. public static void makeFile(String fileName) {
  11. try {
  12. // 尝试在工程 A 执行文件的路径中创建一个新文件
  13. File fs = new File(FOLDER_PATH + "\\" + fileName);
  14. fs.createNewFile();
  15. } catch (AccessControlException e) {
  16. e.printStackTrace();
  17. } catch (IOException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. public static void doPrivilegedAction(final String fileName) {
  22. // 用特权访问方式创建文件
  23. AccessController.doPrivileged(new PrivilegedAction<String>() {
  24. @Override
  25. public String run() {
  26. makeFile(fileName);
  27. return null;
  28. }
  29. });
  30. }
  31. }

创建另一个项目projectY

[java] view plain copy

  1. package com.dusk;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.security.AccessControlException;
  5. import com.dusk.FileUtil;
  6. public class DemoDoPrivilege {
  7. public static void main(String[] args) {
  8. System.out.println("***************************************");
  9. System.out.println("I will show AccessControl functionality...");
  10. System.out.println("Preparation step : turn on system permission check...");
  11. // 打开系统安全权限检查开关
  12. System.setSecurityManager(new SecurityManager());
  13. System.out.println();
  14. System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
  15. System.out.println("Create a new file named temp1.txt via privileged action ...");
  16. // 用特权访问方式在工程 A 执行文件路径中创建 temp1.txt 文件
  17. FileUtil.doPrivilegedAction("temp1.txt");
  18. System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
  19. System.out.println();
  20. System.out.println("/////////////////////////////////////////");
  21. System.out.println("Create a new file named temp2.txt via File ...");
  22. try {
  23. // 用普通文件操作方式在工程 A 执行文件路径中创建 temp2.txt 文件
  24. File fs = new File(
  25. "C:\\Users\\dushangkui\\workspace\\projectX\\temp2.txt");
  26. fs.createNewFile();
  27. } catch (IOException e) {
  28. e.printStackTrace();
  29. } catch (AccessControlException e1) {
  30. e1.printStackTrace();
  31. }
  32. System.out.println("/////////////////////////////////////////");
  33. System.out.println();
  34. System.out.println("-----------------------------------------");
  35. System.out.println("create a new file named temp3.txt via FileUtil ...");
  36. // 直接调用普通接口方式在工程 A 执行文件路径中创建 temp3.txt 文件
  37. FileUtil.makeFile("temp3.txt");
  38. System.out.println("-----------------------------------------");
  39. System.out.println();
  40. System.out.println("***************************************");
  41. }
  42. }

在projectY根目录下面创建策略文件MyPolicy.txt

[java] view plain copy

  1. // 授权工程 A 执行文件路径中文件在本目录中的写文件权限
  2. grant codebase "file:C:/Users/dushangkui/workspace/projectX/bin"
  3. {
  4. permission java.io.FilePermission
  5. "C:\\Users\\dushangkui\\workspace\\projectX\\bin\\*", "write";
  6. };

配置JVM运行参数 -Djava.security.policy=.\\MyPolicy.txt

Java默认不打开安全检查,如果不打开,本地程序拥有所有权限:

[java] view plain copy

  1. ***************************************
  2. I will show AccessControl functionality...
  3. Preparation step : turn on system permission check...
  4. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  5. Create a new file named temp1.txt via privileged action ...
  6. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  7. /////////////////////////////////////////
  8. Create a new file named temp2.txt via File ...
  9. /////////////////////////////////////////
  10. -----------------------------------------
  11. create a new file named temp3.txt via FileUtil ...
  12. -----------------------------------------
  13. ***************************************

如果去掉注释:

[java] view plain copy

  1. ***************************************
  2. I will show AccessControl functionality...
  3. Preparation step : turn on system permission check...
  4. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  5. Create a new file named temp1.txt via privileged action ...
  6. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  7. /////////////////////////////////////////
  8. Create a new file named temp2.txt via File ...
  9. java.security.AccessControlException: access denied ("java.io.FilePermission" "C:\Users\dushangkui\workspace\projectX\temp2.txt" "write")
  10. at java.security.AccessControlContext.checkPermission(Unknown Source)
  11. at java.security.AccessController.checkPermission(Unknown Source)
  12. at java.lang.SecurityManager.checkPermission(Unknown Source)
  13. at java.lang.SecurityManager.checkWrite(Unknown Source)
  14. at java.io.File.createNewFile(Unknown Source)
  15. at com.dusk.DemoDoPrivilege.main(DemoDoPrivilege.java:33)
  16. /////////////////////////////////////////
  17. -----------------------------------------
  18. create a new file named temp3.txt via FileUtil ...
  19. java.security.AccessControlException: access denied ("java.io.FilePermission" "C:\Users\dushangkui\workspace\projectX\bin\temp3.txt" "write")
  20. at java.security.AccessControlContext.checkPermission(Unknown Source)
  21. at java.security.AccessController.checkPermission(Unknown Source)
  22. at java.lang.SecurityManager.checkPermission(Unknown Source)
  23. at java.lang.SecurityManager.checkWrite(Unknown Source)
  24. at java.io.File.createNewFile(Unknown Source)
  25. at com.dusk.FileUtil.makeFile(FileUtil.java:17)
  26. at com.dusk.DemoDoPrivilege.main(DemoDoPrivilege.java:45)
  27. -----------------------------------------
  28. ***************************************

参考: https://www.ibm.com/developerworks/cn/java/j-lo-javasecurity/

原文地址:https://www.cnblogs.com/blackmanbali/p/9032701.html

时间: 2024-08-13 12:55:30

通过AccessController看Java安全模型的相关文章

JAVA安全模型

作为一种诞生于互联网兴起时代的语言,Java 从一开始就带有安全上的考虑,如何保证通过互联网下载到本地的 Java 程序是安全的,如何对 Java 程序访问本地资源权限进行有限授权,这些安全角度的考虑一开始就影响到 Java 语言的设计与实现.可以说 Java 在这些方面的探索与经验,对后来的一些语言与产品都带来了积极影响. 本篇文章中将介绍 Java 中安全模型,以及如何利用安全访问控制机制来实现一些特定目的. Java 中的安全模型 在 Java 中将执行程序分成本地和远程两种,本地代码默认

看Java中==、equals、hashCode的来龙去脉

我有一个哥们去参加了面试,面试官这样问一个Java问题: 你说一下java对象的equals方法调用什么方法呢?我这个哥们想了想,回答说"应该是比较的引用".听了这个答案之后,那个面试官摇头晃脑的说:"不对,你回答的不对,equals方法调用的是hashCode方法".于是乎,我那个技术还不错的哥们就悲壮地栽在这道题目上了. 今天晚上,西安历史上少有的热,那就好好总结一下这个题目的来龙去脉好了,也方便给后面的朋友们提个醒,不要栽在这么简单的问题上.你说这个面试官回答

Java杂谈6——Java安全模型

Java语言安全模型是其有别于传统的编程语言的一个很重要的特点,采用一种沙箱模型隔离了Java的运行环境与具体的操作系统,使得Java在网络环境下能够更为安全的运行.理解Java的安全模型,能够帮助我们站在更高的层面理解Java的底层实现,在适当的时机及时找出问题所在. 沙箱模型的建立主要涉及了Java语言相关的以下几个部分: 类加载器: 运行时包:指由同一个类加载器装载的.属于同一个包的.多类型的集合.任意两个Java类包访问权限适用的前提是:属于同一个包,还必须是属于同一个运行时包.这样规定

从JVM的角度看JAVA代码--代码优化

从JVM的角度看JAVA代码–代码优化 从JVM的角度看JAVA代码代码优化 片段一反复计算 片段二反复比較 在JVM载入优化为class文件,运行class文件时,会有JIT(Just-In-Time)的介入,它会做进一步优化,这样就须要考虑是否可能被JIT优化. 片段一:反复计算 // 反复计算,这里b()被计算两次 Object a = b() == null ? "" : b(); // 优化代码 Object c = b(); Object a = c == null ? &

Java安全模型(沙箱or策略)和transient变量

在研究enum类的源码时,跟踪到Class类的如下代码: T[] getEnumConstantsShared() { if (enumConstants == null) { if (!isEnum()) return null; try { final Method values = getMethod("values"); java.security.AccessController.doPrivileged( new java.security.PrivilegedAction

从stackoverflow上的一个问题看Java动态绑定

我们先来看stackoverflow上的一个问题,先上代码 1 public class Piece{ 2 public static void main (String [] args){ 3 Piece p2 = new Knight(); 4 Knight p1 = new Knight(); 5 p1.capture(p2); 6 p2.capture(p1); 7 } 8 public void capture(){ 9 System.out.println("Capture"

循序渐进看Java web日志跟踪(1)-Tomcat 日志追踪与配置

日志,是软件运行过程中,对各类操作中重要信息的记录. 日志跟踪,不管对于怎么样的项目来说,都是非常重要的一部分,它关系到项目后期的维护和排错,起着举足轻重的作用.项目开发过程中,对日志的记录规则,也将影响到改项目后期维护的难度. 在开发过程中,我们经常遇到的日志记录方式包括有系统输出(System.out,最基础方式),日志框架输出(log4J 等)和分布式日志框架输出等.作为项目的开发者,和后期的管理者来说,对于日志的管理,除了需要知道如何进行日志输出,还需要关注的是,项目后期对日志文件的管理

深入源码看java类加载器ClassLoader

ClassLoader类加载器是负责加载类的对象.ClassLoader 类是一个抽象类.如果给定类的二进制名称(即为包名加类名的全称),那么类加载器会试图查找或生成构成类定义的数据.一般策略是将名称转换为某个文件名,然后从文件系统读取该名称的"类文件".java.lang.ClassLoader类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java 类,即 java.lang.Class类的一个实例.除此之外,ClassLoad

从JVM角度看Java多态

Java多态的三个必要条件: 1. 继承 2. 子类重写父类方法 3. 父类引用指向子类对象 然后看一个例子 输出结果为: 给出结论:当满Java多态的三个条件时,可以发现c.eat()调用的实际上是子类的eat,但c.age调用的还是父类的age,而c.play()则不会通过编译. 但是在java的引用中Father不但指定了c以何种方式访问内存,也规定了能够访问内存空间的大小. 我们看Father实例对象的大小是占两行,但Child实例对象占三行(这里就是简单量化一下). 所以虽然c指向的是