暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Java安全之访问特权-AccessController.doPrivileged

蹲厕所的熊 2018-06-21
2030

蹲厕所的熊 转载请注明原创出处,谢谢!

前言

上一篇文章我们说到了SecurityManager,它主要是通过配置策略文件来进行授权,最后的授权操作调用的是AccessController的checkPermission方法:

  1. public void checkPermission(Permission perm) {

  2.    java.security.AccessController.checkPermission(perm);

  3. }

看了一下API,我们发现AccessController有很多以doPrivileged开头的方法,查看了一下调用发现JDK很多源码都调用了它:

  1. // 代码来自java.net.URLClassLoader

  2. public URL findResource(final String name) {

  3.    /*

  4.     * The same restriction to finding classes applies to resources

  5.     */

  6.    URL url = AccessController.doPrivileged(

  7.            new PrivilegedAction<URL>() {

  8.                public URL run() {

  9.                    return ucp.findResource(name, true);

  10.                }

  11.            }, acc);


  12.    return url != null ? ucp.checkURL(url) : null;

  13. }

这种包装一层在内部使用run方法来执行操作的方式到底有什么作用呢?

Java中的安全模型

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

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

在应用开发中存在很多关于安全的复杂用法,比如最常用的AccessController类的doPrivileged。该方法能够让执行的一段授权方法获得更大的权限(能让跟多的调用方也得到授权),也就是“特权”的含义,在一些特殊场景中非常有用。例如,应用程序可能无法直接访问某些系统资源,但这样的应用程序必须得到这些资源才能够完成功能。针对这种情况,Java SDK提供了doPrivileged方法来让程序突破当前域权限的限制,临时扩大访问权限。下面我们来讲解一下该方法如何使用。

使用

首先我们要在项目a中创建一个类 FileUtil
,用来提供特权创建文件方式和直接创建文件两种方式:

  1. public class FileUtil {

  2.    // 工程 A 执行文件的路径

  3.    private final static String FOLDER_PATH = "/Users/Benjamin/Desktop";


  4.    public static void makeFile(String fileName) {

  5.        try {

  6.            // 尝试在工程 A 执行文件的路径中创建一个新文件

  7.            File fs = new File(FOLDER_PATH + "/" + fileName);

  8.            fs.createNewFile();

  9.        } catch (Exception e) {

  10.            // ignore

  11.        }

  12.    }


  13.    public static void doPrivilegedAction(final String fileName) {

  14.        // 用特权访问方式创建文件

  15.        AccessController.doPrivileged(new PrivilegedAction<String>() {

  16.            @Override

  17.            public String run() {

  18.                makeFile(fileName);

  19.                return null;

  20.            }

  21.        });

  22.    }

  23. }

接着,我们把该项目打成一个jar包供其他项目使用,并把这个jar包放在 /Users/Benjamin/Desktop
下。然后改变默认策略文件的内容(这里我们使用Java自带的策略文件:${java.home}/jre/lib/security/java.policy)

  1. // 授权工程 A 执行文件路径中文件在本目录中的写文件权限

  2. // https://docs.oracle.com/javase/7/docs/technotes/guides/security/PolicyFiles.html

  3. // /-代表目录下所有目录的所有文件,/*仅仅代表目录下这一层级的所有文件

  4. grant codebase "file:/Users/Benjamin/Desktop/-" {

  5.    permission java.io.FilePermission "/Users/Benjamin/Desktop/*", "write";

  6. };

这段配置的意思是允许 a.jar
/Users/Benjamin/Desktop
这个目录下写文件。

接着,我们另起一个项目b,并引入jar包 a.jar
,调用 FileUtil

  1. public class AccessControllerTest {


  2.    public static void main(String[] args) {

  3.        // 打开系统安全权限检查开关

  4.        System.setSecurityManager(new SecurityManager());


  5.        // 1. 用特权访问方式在工程 A 执行文件路径中创建 temp1.txt 文件

  6.        FileUtil.doPrivilegedAction("temp1.txt");


  7.        // 2. 用普通文件操作方式在工程 A 执行文件路径中创建 temp2.txt 文件

  8.        try {

  9.            File fs = new File("/Users/Benjamin/Desktop/temp2.txt");

  10.            fs.createNewFile();

  11.        } catch (Exception e) {

  12.            e.printStackTrace();

  13.        }


  14.        // 3. 直接调用普通接口方式在工程 A 执行文件路径中创建 temp3.txt 文件

  15.        FileUtil.makeFile("temp3.txt");

  16.    }

  17. }

执行main方法,我们会发现桌面只生成了 temp1.txt
文件,而其他文件没有生成,错误信息为:

  1. java.security.AccessControlException: access denied ("java.io.FilePermission" "/Users/Benjamin/Desktop/temp2.txt" "write")

  2.    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)

  3.    at java.security.AccessController.checkPermission(AccessController.java:884)

  4.    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)

  5.    at java.lang.SecurityManager.checkWrite(SecurityManager.java:979)

  6.    at java.io.File.createNewFile(File.java:1008)

  7.    at com.github.security.AccessControllerTest.main(AccessControllerTest.java:28)

  8. java.security.AccessControlException: access denied ("java.io.FilePermission" "/Users/Benjamin/Desktop/temp3.txt" "write")

  9.    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)

  10.    at java.security.AccessController.checkPermission(AccessController.java:884)

  11.    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)

  12.    at java.lang.SecurityManager.checkWrite(SecurityManager.java:979)

  13.    at java.io.File.createNewFile(File.java:1008)

  14.    at com.github.web.FileUtil.makeFile(FileUtil.java:22)

  15.    at com.github.security.AccessControllerTest.main(AccessControllerTest.java:34)

也就是说,如果一个应用开启了安全管理,其他应用想要访问一些授权方法时,必须使用doPrivileged方法开启特权才行。



如果读完觉得有收获的话,欢迎点赞、关注、加公众号【蹲厕所的熊】,查阅更多精彩历史!!!

文章转载自蹲厕所的熊,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论