透過AccessController深入了解Java安全模型

問題

上一期在使用到MappedByteBuffer時,采用的其中一種方式(AccessController)來釋放已分配的內(nèi)存映射,今天具體探討一下AccessController的前世今生贡耽。

回顧上一期代碼:

AccessController.doPrivileged(new PrivilegedAction() {
    public Object run() {
      try {
        Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
        getCleanerMethod.setAccessible(true);
        sun.misc.Cleaner cleaner = (sun.misc.Cleaner)
        getCleanerMethod.invoke(byteBuffer, new Object[0]);
        cleaner.clean();
      } catch (Exception e) {
        e.printStackTrace();
      }
      return null;
    }
});

首先我們先了解一個概念皇筛,在Java的設(shè)計中,實際上是有安全上的考慮殉疼,但是大家在開發(fā)過程中才漆,很少接觸這方面牛曹,也幾乎用不到佛点。所以關(guān)于這方面的材料也不多醇滥,網(wǎng)上找到的基本都是Java安全模型介紹

本文中采用的示例說明基本也摘抄自上述文章超营。

代碼入手

我們先拋開所有概念鸳玩,從代碼入手來看看現(xiàn)在的Java安全模型是如何實現(xiàn)的。

1. 前提

兩個項目演闭,一個X項目不跟,一個Y項目

X項目對于<font color=red>某個目錄(目錄屬于X項目)</font>有寫權(quán)限(該權(quán)限由Java安全模型控制,后續(xù)會講到)

Y項目調(diào)用X項目中的寫文件工具類米碰,且Y項目沒有授權(quán)<font color=red>某個目錄</font>的寫權(quán)限

2. 代碼塊

<b>X項目</b>(項目路徑:D:\workspace\projectX\窝革;代碼路徑在項目路徑bin目錄下;某個目錄:D:\workspace\projectX\bin):

package learn.java.security;

import java.io.File;
import java.io.IOException;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.PrivilegedAction;

public class FileUtil {
   // 工程 A 執(zhí)行文件的路徑
   private final static String FOLDER_PATH = "D:\\workspace\\projectX\\bin";

   public static void makeFile(String fileName) {
       try {
           // 嘗試在工程 A 執(zhí)行文件的路徑中創(chuàng)建一個新文件
           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) {
       // 用特權(quán)訪問方式創(chuàng)建文件
       AccessController.doPrivileged(new PrivilegedAction<String>() {
           @Override
           public String run() {
               makeFile(fileName);
               return null;
           }
       });
   }
}

<b>Y項目</b>(項目路徑:D:\workspace\projectY\吕座;代碼路徑在項目路徑bin目錄下)

package demo.security;

import java.io.File;
import java.io.IOException;
import java.security.AccessControlException;

import learn.java.security.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...");
       // 打開系統(tǒng)安全權(quán)限檢查開關(guān)
       System.setSecurityManager(new SecurityManager());
       System.out.println();

       System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
       System.out.println("
       Create a new file named temp1.txt via privileged action ...");
       // 用特權(quán)訪問方式在工程 A 執(zhí)行文件路徑中創(chuàng)建 temp1.txt 文件
       FileUtil.doPrivilegedAction("temp1.txt");
       System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
       System.out.println();

       System.out.println("http://///////////////////////////////////////");
       System.out.println("Create a new file named temp2.txt via File ...");
       try {
           // 用普通文件操作方式在工程 A 執(zhí)行文件路徑中創(chuàng)建 temp2.txt 文件
           File fs = new File(
                   "D:\\workspace\\projectX\\bin\\temp2.txt");
           fs.createNewFile();
       } catch (IOException e) {
           e.printStackTrace();
       } catch (AccessControlException e1) {
           e1.printStackTrace();
       }
       System.out.println("http://///////////////////////////////////////");
       System.out.println();

       System.out.println("-----------------------------------------");
       System.out.println("create a new file named temp3.txt via FileUtil ...");
       // 直接調(diào)用普通接口方式在工程 A 執(zhí)行文件路徑中創(chuàng)建 temp3.txt 文件
       FileUtil.makeFile("temp3.txt");
       System.out.println("-----------------------------------------");
       System.out.println();

       System.out.println("***************************************");
   }
}

至此代碼已完∨耙耄現(xiàn)在開始對安全開始授權(quán),假設(shè)有以下授權(quán)策略文件(MyPolicy.txt)放在Y工程根目錄下:

// 授權(quán)項目X的Java執(zhí)行文件在其某目錄中的寫文件權(quán)限
grant codebase "file:/D:/workspace/projectX/bin"
{
 permission java.io.FilePermission
   "D:\\workspace\\projectX\\bin\\*", "write";
};

即可指定安全文件運行程序:

java -Djava.security.policy=.\\MyPolicy.txt -classpath
D:\workspace\projectY\bin;D:\workspace\projectX\bin demo.security.DemoDoPrivilege

執(zhí)行結(jié)果:

***************************************
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
     D:\workspace\projectX\bin\temp2.txt write)
    at java.security.AccessController.checkPermission(AccessController.java:108)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:533)
    at java.lang.SecurityManager.checkWrite(SecurityManager.java:963)
    at java.io.File.createNewFile(File.java:882)
    at demo.security.DemoDoPrivilege.main(DemoDoPrivilege.java:32)
////////////////////////////////////////

----------------------------------------
create a new file named temp3.txt via FileUtil ...
java.security.AccessControlException: Access denied (java.io.FilePermission
    D:\workspace\projectX\bin\temp3.txt write)
    at java.security.AccessController.checkPermission(AccessController.java:108)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:533)
    at java.lang.SecurityManager.checkWrite(SecurityManager.java:963)
    at java.io.File.createNewFile(File.java:882)
    at learn.java.security.FileUtil.makeFile(FileUtil.java:16)
    at demo.security.DemoDoPrivilege.main(DemoDoPrivilege.java:43)
----------------------------------------

***************************************

從執(zhí)行結(jié)果可以看出吴趴,當(dāng)安全模式生效時漆诽,Y項目通過普通接口(調(diào)用已有權(quán)限的代碼)、自己創(chuàng)建的方式都無法在沒有權(quán)限的目錄下操作锣枝。只有通過特權(quán)訪問(AccessController)的方式才成功厢拭。

可以看出,要想訪問安全資源撇叁,要么在調(diào)用鏈上權(quán)限齊全(即自己有對應(yīng)的權(quán)限)供鸠,要么就有特權(quán)訪問。

一直在說特權(quán)訪問陨闹,從實現(xiàn)上看已大概了解特權(quán)訪問了楞捂,那實際上又是什么呢家制?

3. 特權(quán)訪問

在Java中執(zhí)行程序分為本地和遠(yuǎn)程兩種,本地代碼默認(rèn)都是可信任的泡一,而遠(yuǎn)程代碼被看作不受信的颤殴。在以前的Java版本中,不授信的遠(yuǎn)程代碼基于沙箱機制鼻忠,只能訪問限定在JVM特定的運行范圍中涵但,并嚴(yán)格控制代碼對本地資源的訪問。而本地代碼可以訪問一切本地資源帖蔓。有效隔離遠(yuǎn)程代碼矮瘟,防止對本地系統(tǒng)造成破壞。

關(guān)于Java安全機制的發(fā)展史可參考上面關(guān)于安全機制的鏈接塑娇。

發(fā)展到現(xiàn)在澈侠,安全模型引入了域的概念。將代碼加載到不同的系統(tǒng)域和應(yīng)用域埋酬,系統(tǒng)域負(fù)責(zé)與關(guān)鍵資源交互哨啃,各應(yīng)用域通過系統(tǒng)域的代理對各種需要的資源進(jìn)行訪問(如上述Y項目通過X項目對資源進(jìn)行寫入)。虛擬機不同的受保護(hù)域写妥,對應(yīng)不一樣的權(quán)限(permission)拳球。存在域中的類文件就有了當(dāng)前域的全部權(quán)限(如上述X項目有X域的權(quán)限而Y項目沒有)


Java最新安全模型.png

看過整個的安全模型,在用法上珍特,最常用就是上述的API(doPrivileged)祝峻,能使一段受信代碼獲得最大權(quán)限,甚至比調(diào)用他的應(yīng)用程序還多(如Y調(diào)用X,明顯比Y權(quán)限多),可臨時訪問更多資源赦颇。

主要用于一些特殊應(yīng)用場景:應(yīng)用無法直接訪問某些系統(tǒng)資源,但應(yīng)用又必須得到這些資源才能完成功能奥溺。doPrivileged讓程序突破當(dāng)前域權(quán)限限制,臨時擴大訪問權(quán)限症脂。

AccessController的工作機制:

在某個線程調(diào)用棧中谚赎,當(dāng)AccessController的checkPermission方法被最近調(diào)用程序(如上述代碼創(chuàng)建文件時)調(diào)用時,對于程序要求的訪問權(quán)限诱篷,ACC決定是否授權(quán)算法如下:

  1. 如果調(diào)用鏈中某個調(diào)用程序沒有需要的權(quán)限壶唤,拋出AccessControlException(如上述代碼Y無權(quán)限)
  2. 滿足以下情況即被授權(quán)
  • 調(diào)用程序訪問另一個有該權(quán)限的方法,且此方法標(biāo)記為有訪問特權(quán)(如上述代碼Y調(diào)用X的特權(quán)方法)
  • 調(diào)用程序調(diào)用的(直接或間接)后續(xù)對象有權(quán)限(如上述代碼X有權(quán)限)

回歸問題本質(zhì)

我們mmap也沒有使用外部遠(yuǎn)程代碼棕所,何必用AccessController這么復(fù)雜闸盔,直接把AccessController去掉試一下。實際上看貌似也是沒問題的琳省。那為什么那么多的例子都是用AccessController來釋放資源呢迎吵?

但由于Cleaner是在rt.jar包中躲撰,所以有可能會出現(xiàn)沒有權(quán)限訪問的情況,jvm把其加入不同域中击费。暫時來看只是猜測拢蛋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蔫巩,隨后出現(xiàn)的幾起案子谆棱,更是在濱河造成了極大的恐慌,老刑警劉巖圆仔,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垃瞧,死亡現(xiàn)場離奇詭異,居然都是意外死亡坪郭,警方通過查閱死者的電腦和手機个从,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來歪沃,“玉大人嗦锐,你說我怎么就攤上這事〕衤蓿” “怎么了意推?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長珊蟀。 經(jīng)常有香客問我,道長外驱,這世上最難降的妖魔是什么育灸? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮昵宇,結(jié)果婚禮上磅崭,老公的妹妹穿的比我還像新娘。我一直安慰自己瓦哎,他們只是感情好砸喻,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蒋譬,像睡著了一般割岛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上犯助,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天癣漆,我揣著相機與錄音,去河邊找鬼剂买。 笑死惠爽,一個胖子當(dāng)著我的面吹牛癌蓖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播婚肆,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼租副,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了较性?” 一聲冷哼從身側(cè)響起附井,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎两残,沒想到半個月后永毅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡人弓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年沼死,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片崔赌。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡意蛀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出健芭,到底是詐尸還是另有隱情县钥,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布慈迈,位于F島的核電站若贮,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏痒留。R本人自食惡果不足惜谴麦,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望伸头。 院中可真熱鬧匾效,春花似錦、人聲如沸恤磷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扫步。三九已至魔策,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锌妻,已是汗流浹背代乃。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搁吓。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓原茅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親堕仔。 傳聞我的和親對象是個殘疾皇子擂橘,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359