日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java安全沙箱(一)之ClassLoader双亲委派机制

發布時間:2025/3/21 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java安全沙箱(一)之ClassLoader双亲委派机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?java是一種類型安全的語言,它有四類稱為安全沙箱機制的安全機制來保證語言的安全性,這四類安全沙箱分別是:

  • 類加載體系

  • .class文件檢驗器

  • 內置于Java虛擬機(及語言)的安全特性

  • 安全管理器及Java API

本篇博客主要介紹“類加載體系”的基本原理;如需了解其它幾類安全機制可以通過上面的博客鏈接進入查看。

簡介

“類加載體系”及ClassLoader雙親委派機制。java程序中的 .java文件編譯完會生成 .class文件,而 .class文件就是通過被稱為類加載器的ClassLoader加載的,而ClassLoder在加載過程中會使用“雙親委派機制”來加載 .class文件,先上圖:

看著圖從上往下介紹:

  • BootStrapClassLoader:啟動類加載器,該ClassLoader是jvm在啟動時創建的,用于加載?$JAVA_HOME/jre/lib下面的類庫(或者通過參數-Xbootclasspath指定)。由于引導類加載器涉及到虛擬機本地實現細節,開發者無法直接獲取到啟動類加載器的引用,所以不能直接通過引用進行操作。

  • ExtClassLoader:擴展類加載器,該ClassLoader是在sun.misc.Launcher里作為一個內部類ExtClassLoader定義的(即?sun.misc.Launcher$ExtClassLoader),ExtClassLoader會加載?$JAVA_HOME/jre/lib/ext下的類庫(或者通過參數-Djava.ext.dirs指定)。

  • AppClassLoader:應用程序類加載器,該ClassLoader同樣是在sun.misc.Launcher里作為一個內部類AppClassLoader定義的(即?sun.misc.Launcher$AppClassLoader),AppClassLoader會加載java環境變量CLASSPATH所指定的路徑下的類庫,而CLASSPATH所指定的路徑可以通過System.getProperty("java.class.path")獲取;當然,該變量也可以覆蓋,可以使用參數-cp,例如:java -cp 路徑 (可以指定要執行的class目錄)。

  • CustomClassLoader:自定義類加載器,該ClassLoader是指我們自定義的ClassLoader,比如tomcat的StandardClassLoader屬于這一類;當然,大部分情況下使用AppClassLoader就足夠了。

  • ClassLoader初始化源碼

    下面貼下jdk關于類加載的源碼,上述四種類加載器中CustomClassLoader是用戶自定義的,BootStrapClassLoader是jvm創建的,就不展示了;這里展示下AppClassLoader和ExtClassLoader的啟動過程,前面介紹過,AppClassLoader和ExtClassLoader都是在sun.misc.Launcher里定義的,而我的sun.misc.Launcher沒有源碼,大家將就看看反編譯的代碼吧。如果想看sun.*包下的類源碼,大家可以下載openjdk來查看。

    public Launcher(){ExtClassLoader extclassloader;try{extclassloader = ExtClassLoader.getExtClassLoader();}catch(IOException ioexception) {throw new InternalError("Could not create extension class loader");}try{loader = AppClassLoader.getAppClassLoader(extclassloader);}catch(IOException ioexception1){throw new InternalError("Could not create application class loader");}Thread.currentThread().setContextClassLoader(loader);String s = System.getProperty("java.security.manager");if(s != null){SecurityManager securitymanager = null;if("".equals(s) || "default".equals(s))securitymanager = new SecurityManager();elsetry{securitymanager = (SecurityManager)loader.loadClass(s).newInstance();}catch(IllegalAccessException illegalaccessexception) { }catch(InstantiationException instantiationexception) { }catch(ClassNotFoundException classnotfoundexception) { }catch(ClassCastException classcastexception) { }if(securitymanager != null)System.setSecurityManager(securitymanager);elsethrow new InternalError((new StringBuilder()).append("Could not create SecurityManager: ").append(s).toString());}}

    可以看到在Launcher構造函數的執行過程如下:

  • 通過ExtClassLoader.getExtClassLoader()創建了ExtClassLoader;

  • 通過AppClassLoader.getAppClassLoader(ExtClassLoader)創建了AppClassLoader,并將ExtClassLoader設為AppClassLoader的parent?ClassLoader;

  • 通過Thread.currentThread().setContextClassLoader(loader)把AppClassLoader設為線程的上下文 ClassLoader;

  • 根據jvm參數-Djava.security.manager創建安全管理器(安全管理器的相關內容會在后續博客安全管理器及Java API中介紹),此時jvm會設置系統屬性"java.security.manager"為空字符串""。

  • 再貼下ExtClassLoader源碼:

    public static ExtClassLoader getExtClassLoader()throws IOException{File afile[] = getExtDirs();return (ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction(afile) {public Object run() throws IOException{int i = dirs.length;for(int j = 0; j < i; j++)MetaIndex.registerDirectory(dirs[j]);return new ExtClassLoader(dirs);}final File val$dirs[];{dirs = afile;super();}});PrivilegedActionException privilegedactionexception;privilegedactionexception;throw (IOException)privilegedactionexception.getException();}private static File[] getExtDirs(){String s = System.getProperty("java.ext.dirs");File afile[];if(s != null){StringTokenizer stringtokenizer = new StringTokenizer(s, File.pathSeparator);int i = stringtokenizer.countTokens();afile = new File[i];for(int j = 0; j < i; j++)afile[j] = new File(stringtokenizer.nextToken());} else{afile = new File[0];}return afile;}

    ?

    反編譯的源碼,大家將就看下;這里大家關注下getExtDirs()這個方法,它會獲取屬性"java.ext.dirs"所對應的值,然后通過系統分隔符分割,然后加載分割后的字符串對應的目錄作為ClassLoader的類加載庫。

    下面看看AppClassLoader源碼:

    public static ClassLoader getAppClassLoader(ClassLoader classloader) throws IOException{String s = System.getProperty("java.class.path");File afile[] = s != null ? Launcher.getClassPath(s) : new File[0];return (AppClassLoader)AccessController.doPrivileged(new PrivilegedAction(s, afile, classloader) {public Object run() {URL aurl[] = s != null ? Launcher.pathToURLs(path) : new URL[0];return new AppClassLoader(aurl, extcl);}final String val$s;final File val$path[];final ClassLoader val$extcl;{s = s1;path = afile;extcl = classloader;super();}});}

    ?

    ?

    首先獲取"java.class.path"對應的屬性,并轉換為URL[]并設置為ClassLoader的類加載庫,注意這里的方法入參classloader就是ExtClassLoader,在創AppClassLoader會傳入ExtClassLoader作為parent?ClassLoader。

    上面就是ClassLoader的啟動和初始化過程,后面會把loader作為應用程序的默認ClassLoader使用,看下面的測試用例:

    package com.jvm.study.classload;public class Test {public static void main(String... args) {ClassLoader loader = Test.class.getClassLoader();System.err.println("loader:"+loader);while (loader != null) {loader = loader.getParent();System.err.println("in while:"+loader);}} }

    可以看到ClassLoader的層次結構,輸出結果為:

    ?

    ClassLoader雙親委派機制源碼

    前面談到了ClassLoader的幾類加載器,而ClassLoader使用雙親委派機制來加載class文件的。

    ClassLoader的雙親委派機制是這樣的(這里先忽略掉自定義類加載器CustomClassLoader):

  • 當AppClassLoader加載一個class時,它首先不會自己去嘗試加載這個類,而是把類加載請求委派給父類加載器ExtClassLoader去完成。

  • 當ExtClassLoader加載一個class時,它首先也不會自己去嘗試加載這個類,而是把類加載請求委派給BootStrapClassLoader去完成。

  • 如果BootStrapClassLoader加載失敗(例如在$JAVA_HOME/jre/lib里未查找到該class),會使用ExtClassLoader來嘗試加載;

  • 若ExtClassLoader也加載失敗,則會使用AppClassLoader來加載,如果AppClassLoader也加載失敗,則會報出異常ClassNotFoundException。

  • 下面貼下ClassLoader的loadClass(String name, boolean resolve)源碼:

    protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {// First, check if the class has already been loadedClass c = findLoadedClass(name);if (c == null) {try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader }if (c == null) {// If still not found, then invoke findClass in order// to find the class.c = findClass(name);}}if (resolve) {resolveClass(c);}return c;}

    代碼很明朗:首先找緩存(findLoadedClass),沒有的話就判斷有沒有parent,有的話就用parent來遞歸的loadClass,然而ExtClassLoader并沒有設置parent,則會通過findBootstrapClassOrNull來加載class,而findBootstrapClassOrNull則會通過JNI方法”private native Class findBootstrapClass(String name)“來使用BootStrapClassLoader來加載class。

    然后如果parent未找到class,則會調用findClass來加載class,findClass是一個protected的空方法,可以覆蓋它以便自定義class加載過程。

    另外,雖然ClassLoader加載類是使用loadClass方法,但是鼓勵用 ClassLoader 的子類重寫 findClass(String),而不是重寫loadClass,這樣就不會覆蓋了類加載默認的雙親委派機制。

    雙親委派機制為什么安全

    前面談到雙親委派機制是為了安全而設計的,但是為什么就安全了呢?舉個例子,ClassLoader加載的class文件來源很多,比如編譯器編譯生成的class、或者網絡下載的字節碼。而一些來源的class文件是不可靠的,比如我可以自定義一個java.lang.Integer類來覆蓋jdk中默認的Integer類,例如下面這樣:

    package java.lang;/*** hack*/ public class Integer {public Integer(int value) {System.exit(0);} }

    初始化這個Integer的構造器是會退出JVM,破壞應用程序的正常進行,如果使用雙親委派機制的話該Integer類永遠不會被調用,以為委托BootStrapClassLoader加載后會加載JDK中的Integer類而不會加載自定義的這個,可以看下下面這測試個用例:

    public static void main(String... args) {Integer i = new Integer(1);System.err.println(i);}

    執行時JVM并未在new Integer(1)時退出,說明未使用自定義的Integer,于是就保證了安全性。

    轉載于:https://www.cnblogs.com/duanxz/p/6108343.html

    總結

    以上是生活随笔為你收集整理的java安全沙箱(一)之ClassLoader双亲委派机制的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。