java安全管理器類SecurityManager簡單剖析:
javadoc介紹:
SecurityManager是一個允許應用實現一種安全策略的類。它允許一個應用去明確,在執行一個可能安全或者敏感的操作之前,此操作是否允許在一個安全的上下文中被執行。
應用可以同意或者拒絕執行操作。
SecurityManager類包含許多以check開頭命名的方法。java庫中的各種方法在執行一些敏感的操作時可以調用這些方法。對check方法典型的調用如下:
SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkXXX(argument); }
SecurityManager通過拋出異常來阻止沒有權限或敏感操作的完成。 如果操作被允許執行,則簡單的返回,如果操作被拒絕,則拋出一個SecurityException。
對於這種處理方式唯一的例外就是checkTopLevelWindow方法,此方法返回boolean值。
當前安全管理器被設置通過System類的setSecurityManager方法。獲取當前安全管理器用System類的getSecurityManager方法。
SecurityManager中特定的方法checkPermission(java.security.Permission)負責明確允許還是拒絕由指定權限所指示的訪問請求,默認的實現是:
AccessController.checkPermission(perm);
若果一個請求訪問被允許,則checkPermission安靜的返回,如果被拒絕,則拋出一個SecurityException異常。
從Java 2 SDK v1.2 開始,SecurityManager 中其他所有 check 方法的默認實現都是調用 SecurityManager checkPermission
方法來確定調用線程是否具有執行所請求操作的權限。
注意,只帶有單個權限參數的 checkPermission 方法總是在當前執行的線程上下文中執行安全檢查。
有時,應該在給定上下文中進行的安全檢查實際上需要在不同 的上下文(例如,在一個輔助線程中)中進行。
Java 為這種情況提供了包含有上下文參數的 getSecurityContext方法和 checkPermission方法。
getSecurityContext 方法返回當前調用上下文的一個“快照”(默認的實現返回一個 AccessControlContext 對象)。下面是一個示例調用:
Object context = null; SecurityManager sm = System.getSecurityManager(); if (sm != null) context = sm.getSecurityContext();
checkPermission 方法使用一個上下文對象,以及根據該上下文而不是當前執行線程的上下文作出訪問決策的權限。
因此另一個上下文中的代碼可以調用此方法,傳遞權限和以前保存的上下文對象。下面是一個示例調用,它使用了以前示例中獲得的 SecurityManager sm:
if (sm != null) sm.checkPermission(permission, context);
權限分為以下類別:文件、套接字、網絡、安全性、運行時、屬性、AWT、反射和可序列化。管理各種權限類別的類是
java.io.FilePermission、
java.net.SocketPermission、
java.net.NetPermission、
java.security.SecurityPermission、
java.lang.RuntimePermission、
java.util.PropertyPermission、
java.awt.AWTPermission、
java.lang.reflect.ReflectPermission
java.io.SerializablePermission
除前兩個(FilePermission 和 SocketPermission)類以外的所有類都是 java.security.BasicPermission 的子類,
而 java.security.BasicPermission 類又是頂級權限類 java.security.Permission 的抽象子類. BasicPermission 定義了所有權限所需的功能,
這些功能的名稱遵從分層屬性命名慣例(例如“exitVM”、“setFactory”、“queuePrintJob”等等)。
在名稱的末尾可能出現一個星號,前面是“.”或星號,這表示通配符匹配。例如:“a.*”、“*”是有效的,而“*a”或“a*b”是無效的。
FilePermission 和 SocketPermission 是頂級權限類 (java.security.Permission) 的子類。像這些命名語法比 BasicPermission
所用的語法更為復雜的類都直接是 Permission 的子類,而不是 BasicPermission 的子類。例如,對於 java.io.FilePermission 對象而言,權限名就是文件(或目錄)的路徑名。
某些權限類具有一個“動作”列表,告知允許對象所執行的動作。例如,對於 java.io.FilePermission 對象,
動作列表(如“讀、寫”)指定了允許對指定文件(或指定目錄中的文件)執行哪些動作。
其他權限類是“指定的”權限 - 有名稱但沒有動作列表的類;您也許有指定的權限,也許沒有。
注:還有一個暗指所有權限的 java.security.AllPermission 權限。該權限是為了簡化系統管理員的工作而存在的,因為管理員可能需要執行很多需要所有(或許多)權限的任務。
SecurityManager應用場景:
當運行未知的Java程序的時候,該程序可能有惡意代碼(刪除系統文件、重啟系統等),為了防止運行惡意代碼對系統產生影響,需要對運行的代碼的權限進行控制,這時候就要啟用Java安全管理器。
啟動安全管理器:
- 參數啟動(指定 -Djava.security.manager)
- 通過程序打開SecurityManager SecurityManager sm = new SecurityManager(); System.setSecurityManager(sm);
關閉安全管理器:
程序關閉: SecurityManager sm = System.getSecurityManager(); if(sm != null){ System.setSecurityManager(null); }
注意:上面的代碼只有你在位於${JDK_HOME}/jre/lib/security目錄下或者其他指定目錄下的java.policy文件中指定了一個權限才會奏效。 這個權限是:
permission java.lang.RuntimePermission "setSecurityManager";
默認配置文件:(默認的安全管理器配置文件是 $JAVA_HOME/jre/lib/security/java.policy,即當未指定配置文件時,將會使用該配置。內容如下:)
// Standard extensions get all permissions by default grant codeBase "file:${{java.ext.dirs}}/*" { permission java.security.AllPermission; }; // default permissions granted to all domains grant { // Allows any thread to stop itself using the java.lang.Thread.stop() // method that takes no argument. // Note that this permission is granted by default only to remain // backwards compatible. // It is strongly recommended that you either remove this permission // from this policy file or further restrict it to code sources // that you specify, because Thread.stop() is potentially unsafe. // See the API specification of java.lang.Thread.stop() for more // information. permission java.lang.RuntimePermission "stopThread"; // allows anyone to listen on dynamic ports permission java.net.SocketPermission "localhost:0", "listen"; // "standard" properies that can be read by anyone permission java.util.PropertyPermission "java.version", "read"; permission java.util.PropertyPermission "java.vendor", "read"; permission java.util.PropertyPermission "java.vendor.url", "read"; permission java.util.PropertyPermission "java.class.version", "read"; permission java.util.PropertyPermission "os.name", "read"; permission java.util.PropertyPermission "os.version", "read"; permission java.util.PropertyPermission "os.arch", "read"; permission java.util.PropertyPermission "file.separator", "read"; permission java.util.PropertyPermission "path.separator", "read"; permission java.util.PropertyPermission "line.separator", "read"; permission java.util.PropertyPermission "java.specification.version", "read"; permission java.util.PropertyPermission "java.specification.vendor", "read"; permission java.util.PropertyPermission "java.specification.name", "read"; permission java.util.PropertyPermission "java.vm.specification.version", "read"; permission java.util.PropertyPermission "java.vm.specification.vendor", "read"; permission java.util.PropertyPermission "java.vm.specification.name", "read"; permission java.util.PropertyPermission "java.vm.version", "read"; permission java.util.PropertyPermission "java.vm.vendor", "read"; permission java.util.PropertyPermission "java.vm.name", "read"; };
配置文件簡單解釋:
配置基本原則:
在啟用安全管理器的時候,配置遵循以下基本原則:
- 沒有配置的權限表示沒有。
- 只能配置有什么權限,不能配置禁止做什么。
- 同一種權限可多次配置,取並集。
- 統一資源的多種權限可用逗號分割。
默認配置文件解釋:
第一部分授權:授權基於路徑在"file:${{java.ext.dirs}}/*"的class和jar包,所有權限。
grant codeBase "file:${{java.ext.dirs}}/*" {
permission java.security.AllPermission;
};
第二部分授權:這是細粒度的授權,對某些資源的操作進行授權。具體不再解釋,可以查看javadoc
grant { permission java.lang.RuntimePermission "stopThread"; …… }
補充:當批量配置的時候(例如第一部分授權),有三種模式:
- directory/ 表示directory目錄下的所有.class文件,不包括.jar文件
- directory/* 表示directory目錄下的所有的.class及.jar文件
- directory/- 表示directory目錄下的所有的.class及.jar文件,包括子目錄
可以通過${}來引用系統屬性,如: "file:${{java.ext.dirs}}/*"
問題解決:
當出現關於安全管理的報錯的時候,基本有兩種方式來解決。
取消安全管理器:
一般情況下都是無意啟動安全管理器,所以這時候只需要把安全管理器進行關閉,去掉啟動參數即可。
增加相應權限:
若因為沒有權限報錯,則報錯信息中會有請求的權限和請求什么權限,如下:
Exception in thread "main" java.security.AccessControlException: access denied (java.io.FilePermission E:\pack\a\a.txt write)
上面例子,請求資源E:\pack\a\a.txt,的FilePermission的寫權限沒有,因此被拒絕。
也可以開放所有權限:
grant {
permission java.security.AllPermission;
};