作为一种诞生于互联网兴起时代的语言,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
- for (int i = m; i > 0; i--) {
- if (caller i‘s domain does not have the permission)
- throw AccessControlException
- else if (caller i is marked as privileged) {
- if (a context was specified in the call to doPrivileged)
- context.checkPermission(permission)
- if (limited permissions were specified in the call to doPrivileged) {
- for (each limited permission) {
- if (the limited permission implies the requested permission)
- return;
- }
- } else
- return;
- }
- }
- // Next, check the context inherited when the thread was created.
- // Whenever a new thread is created, the AccessControlContext at
- // that time is stored and associated with the new thread, as the
- // "inherited" context.
- inheritedContext.checkPermission(permission);
在某一个线程的调用栈中,当 AccessController 的 checkPermission 方法被最近的调用程序(例如 A 类中的方法)调用时,对于程序要求的所有访问权限,ACC 决定是否授权的基本算法如下:
1. 如果调用链中的某个调用程序没有所需的权限,将抛出 AccessControlException;
2. 若是满足以下情况即被授予权限:
a. 调用程序访问另一个有该权限域里程序的方法,并且此方法标记为有访问“特权”;
b. 调用程序所调用(直接或间接)的后续对象都有上述权限。
当然了,Java SDK 给域提供了 doPrivileged 方法,让程序突破当前域权限限制,临时扩大访问权限。
创建一个项目projectX:
[java] view plain copy
- package com.dusk;
- import java.io.File;
- import java.io.IOException;
- import java.security.AccessControlException;
- import java.security.AccessController;
- import java.security.PrivilegedAction;
- public class FileUtil {
- // 工程 A 执行文件的路径
- private final static String FOLDER_PATH = "C:\\Users\\dushangkui\\workspace\\projectX\\bin";
- public static void makeFile(String fileName) {
- try {
- // 尝试在工程 A 执行文件的路径中创建一个新文件
- File fs = new File(FOLDER_PATH + "\\" + fileName);
- fs.createNewFile();
- } catch (AccessControlException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- public static void doPrivilegedAction(final String fileName) {
- // 用特权访问方式创建文件
- AccessController.doPrivileged(new PrivilegedAction<String>() {
- @Override
- public String run() {
- makeFile(fileName);
- return null;
- }
- });
- }
- }
创建另一个项目projectY
[java] view plain copy
- package com.dusk;
- import java.io.File;
- import java.io.IOException;
- import java.security.AccessControlException;
- import com.dusk.FileUtil;
- public class DemoDoPrivilege {
- public static void main(String[] args) {
- System.out.println("***************************************");
- System.out.println("I will show AccessControl functionality...");
- System.out.println("Preparation step : turn on system permission check...");
- // 打开系统安全权限检查开关
- System.setSecurityManager(new SecurityManager());
- System.out.println();
- System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
- System.out.println("Create a new file named temp1.txt via privileged action ...");
- // 用特权访问方式在工程 A 执行文件路径中创建 temp1.txt 文件
- FileUtil.doPrivilegedAction("temp1.txt");
- System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
- System.out.println();
- System.out.println("/////////////////////////////////////////");
- System.out.println("Create a new file named temp2.txt via File ...");
- try {
- // 用普通文件操作方式在工程 A 执行文件路径中创建 temp2.txt 文件
- File fs = new File(
- "C:\\Users\\dushangkui\\workspace\\projectX\\temp2.txt");
- fs.createNewFile();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (AccessControlException e1) {
- e1.printStackTrace();
- }
- System.out.println("/////////////////////////////////////////");
- System.out.println();
- System.out.println("-----------------------------------------");
- System.out.println("create a new file named temp3.txt via FileUtil ...");
- // 直接调用普通接口方式在工程 A 执行文件路径中创建 temp3.txt 文件
- FileUtil.makeFile("temp3.txt");
- System.out.println("-----------------------------------------");
- System.out.println();
- System.out.println("***************************************");
- }
- }
在projectY根目录下面创建策略文件MyPolicy.txt
[java] view plain copy
- // 授权工程 A 执行文件路径中文件在本目录中的写文件权限
- grant codebase "file:C:/Users/dushangkui/workspace/projectX/bin"
- {
- permission java.io.FilePermission
- "C:\\Users\\dushangkui\\workspace\\projectX\\bin\\*", "write";
- };
配置JVM运行参数 -Djava.security.policy=.\\MyPolicy.txt
Java默认不打开安全检查,如果不打开,本地程序拥有所有权限:
[java] view plain copy
- ***************************************
- I will show AccessControl functionality...
- Preparation step : turn on system permission check...
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Create a new file named temp1.txt via privileged action ...
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- /////////////////////////////////////////
- Create a new file named temp2.txt via File ...
- /////////////////////////////////////////
- -----------------------------------------
- create a new file named temp3.txt via FileUtil ...
- -----------------------------------------
- ***************************************
如果去掉注释:
[java] view plain copy
- ***************************************
- I will show AccessControl functionality...
- Preparation step : turn on system permission check...
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Create a new file named temp1.txt via privileged action ...
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- /////////////////////////////////////////
- Create a new file named temp2.txt via File ...
- java.security.AccessControlException: access denied ("java.io.FilePermission" "C:\Users\dushangkui\workspace\projectX\temp2.txt" "write")
- 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.checkWrite(Unknown Source)
- at java.io.File.createNewFile(Unknown Source)
- at com.dusk.DemoDoPrivilege.main(DemoDoPrivilege.java:33)
- /////////////////////////////////////////
- -----------------------------------------
- create a new file named temp3.txt via FileUtil ...
- java.security.AccessControlException: access denied ("java.io.FilePermission" "C:\Users\dushangkui\workspace\projectX\bin\temp3.txt" "write")
- 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.checkWrite(Unknown Source)
- at java.io.File.createNewFile(Unknown Source)
- at com.dusk.FileUtil.makeFile(FileUtil.java:17)
- at com.dusk.DemoDoPrivilege.main(DemoDoPrivilege.java:45)
- -----------------------------------------
- ***************************************
参考: https://www.ibm.com/developerworks/cn/java/j-lo-javasecurity/
原文地址:https://www.cnblogs.com/blackmanbali/p/9032701.html