1. 程式人生 > >java之jvm學習筆記四(安全管理器)

java之jvm學習筆記四(安全管理器)

               前面已經簡述了java的安全模型的兩個組成部分(類裝載器,class檔案校驗器),接下來學習的是java安全模型的另外一個重要組成部分安全管理器。

               安全管理器是一個單獨的物件,在java虛擬機器中,它在訪問控制-對於外部資源的訪問控制-起到中樞作用

                如果光看概念可能並不能很好的理解,或者說比較抽象,下面是ClassLoader其中的一個建構函式,先簡單的看看它在初始化ClassLoader之前會做一些什麼操作

  1. protected ClassLoader(ClassLoader parent) {  
  2.     SecurityManager security = System.getSecurityManager();  
  3.     if (security != null) {  
  4.         security.checkCreateClassLoader();  
  5.     }  
  6.     this.parent = parent;  
  7.     initialized = true;  
  8.     }  

這個建構函式的第一話(當然還有隱式呼叫)就是System.getSecurityManager();這行程式碼返回的就是一個安全管理器物件security,這個物件所屬的目錄為java.lang.SecurityManager。

這個建構函式先判斷如果已經安裝了安全管理器security(在前面類裝載器的章節,我們提到過,類裝載器和安全管理器是可以由使用者定製的,在這裡有了體現吧!!既然有System.getSecurityManager();你當然也應該猜到有System.setSecurityManager();),也就是安全管理器不為空,那麼就執行校驗,跳到checkCreateClassLoader();看看他做的是什麼操作

  1. publicvoid checkCreateClassLoader() {  
  2. heckPermission(SecurityConstants.CREATE_CLASSLOADER_PERMISSION);  
  3.   }  


這裡又呼叫了另外一個方法,從方法名字上,就可以猜到這個方法是用來校驗許可權的,校驗是否有建立ClassLoader的許可權,再跳到checkPermisson方法裡

  1. publicstaticvoid checkPermission(Permission perm)  
  2.    throws AccessControlException   
  3.     {  
  4.  //System.err.println("checkPermission "+perm);
  5.  //Thread.currentThread().dumpStack(); if (perm == null) {
  6.      thrownew NullPointerException("permission can't be null");  
  7.  } AccessControlContext stack = getStackAccessControlContext();  
  8.  // if context is null, we had privileged system code on the stack.
  9.  if (stack == null) {  
  10.      Debug debug = AccessControlContext.getDebug();  
  11.      boolean dumpDebug = false;  
  12.      if (debug != null) {  
  13.   dumpDebug = !Debug.isOn("codebase=");  
  14.   dumpDebug &= !Debug.isOn("permission=") ||  
  15.       Debug.isOn("permission=" + perm.getClass().getCanonicalName());  
  16.      }     if (dumpDebug && Debug.isOn("stack")) {  
  17.   Thread.currentThread().dumpStack();  
  18.      }     if (dumpDebug && Debug.isOn("domain")) {  
  19.   debug.println("domain (context is null)");  
  20.      }     if (dumpDebug) {  
  21.   debug.println("access allowed "+perm);  
  22.      }  
  23.      return;  
  24.  } AccessControlContext acc = stack.optimize();  
  25.  acc.checkPermission(perm);  
  26.     }  
  27. }  

上面的這個方法有些程式碼比較難以理解,我們不用每行都讀懂(這個方法涉及的東西比較多,它涉及到了程式碼簽名認證,策略還有保護域,這些我們在後一節中會詳細的講解,看不懂先跳過),看它的註解// if context is null, we had privileged system code on the stack.意思就是如果當前的訪問控制器上下文為空,在棧上的系統程式碼將得到特權,找到acc.checkPermission(perm);再跳進去找到下面這段程式碼

  1. /* 
  2.   * iterate through the ProtectionDomains in the context. 
  3.   * Stop at the first one that doesn't allow the 
  4.   * requested permission (throwing an exception). 
  5.   * 
  6.   *//* if ctxt is null, all we had on the stack were system domains, 
  7.     or the first domain was a Privileged system domain. This 
  8.     is to make the common case for system code very fast */if (context == null)  
  9.      returnfor (int i=0; i< context.length; i++) {  
  10.      if (context[i] != null &&  !context[i].implies(perm)) {  
  11.   if (dumpDebug) {  
  12.       debug.println("access denied " + perm);  
  13.   }  if (Debug.isOn("failure") && debug != null) {  
  14.       // Want to make sure this is always displayed for failure,
  15.       // but do not want to display again if already displayed
  16.       // above.
  17.       if (!dumpDebug) {  
  18.    debug.println("access denied " + perm);  
  19.       }  
  20.       Thread.currentThread().dumpStack();  
  21.       final ProtectionDomain pd = context[i];  
  22.       final Debug db = debug;  
  23.       AccessController.doPrivileged (new PrivilegedAction() {  
  24.    public Object run() {  
  25.        db.println("domain that failed "+pd);  
  26.        returnnull;  
  27.    }  
  28.       });  
  29.   }  
  30.   thrownew AccessControlException("access denied "+perm, perm);  
  31.      }  
  32.  }  

什麼都不用看,就看最上面的那段註解,意思是遍歷上下文中的保護域,一旦發現請求的許可權不被允許,停止,丟擲異常,到這裡我們有一個比較清晰的概念了,安全管理器就是用來控制執行許可權的,而上面的這段程式碼中有一個很重要的類 AccessController訪問控制器,還有一個很重要的名詞保護域(保護域我們在前面一節也有簡單的帶過一下,是不是有點印象),這些可能現在聽有點模糊,不要擔心,暫時不要管,後面一章節慢慢的會對他們進行講解。


好了瞭解安全管理器是做什麼的之後,接下來,來做一個下的實驗,先來驗證,預設安全管理是沒有被安裝的,接著來試著把他安裝上去。在我的環境中我是沒有安裝預設的安全管理器的,也沒有基於預設的安全管理器寫自己的安全管理器,如果需要開啟的話,可以在程式顯示的安裝安全管理器,同樣可以讓它自動安裝預設的安全管理器(給jvm加上-Djava.security.manager就可以了。

下面我們用熟悉的ecplise寫一個簡單的demo來看看安裝前後的區別,在下一節中,會詳細的來學習程式碼簽名認證和策略,並寫一個自己的安全管理器。

  1. <span style="font-size:14px;">  publicstaticvoid main(String[] args) {  
  2.         System.out.println(System.getSecurityManager());  
  3.     }</span>  

執行這個main函式,輸出什麼?是的輸出null,這個時候我們沒有安裝預設的安全管理器

重新換個方式執行,在ecplise裡右鍵--Run As--Run Configuration--Arguments,在VM arguments的欄目裡輸入

-Djava.security.manager。在點選Run,這個時候看到什麼?

輸出:securityManager的物件名。這個時候預設的安全管理器就被安裝上了。

總結:

      在java虛擬機器中,它在訪問控制-對於外部資源的訪問控制-起到中樞作用