每個Java應用都可以有自己的安全管理器,它是防範惡意攻擊的主要安全衛士。安全管理器通過執行運行階段檢查和訪問授權,以實施應用所需的安全性原則,從而保護資源免受惡意操作的攻擊。實際上,安全管理器根據Java安全性原則檔案決定將哪組許可權授予類。然而,當不可信的類和第三方應用使用JVM時,Java安全管理器將使用與JVM相關的安全性原則來識別惡意操作。在很多情況下,威脅模型不包含運行於JVM中的惡意代碼,此時Java安全管理器便不是必需的。當安全管理器檢測到違反安全性原則的操作時,JVM將引發AccessControlException或SecurityException。
在Java應用中,安全管理器是由System類中的方法setSecurityManager設定的。要獲得當前的安全管理器,可以使用方法getSecurityManager。
java.lang.SecurityManager類包含了很多checkXXXX方法,如用於判斷對檔案存取權限的checkRead(String file)方法。這些檢查方法調用SecurityManager.checkPermission方法,後者根據安全性原則檔案判斷調用應用是否有執行所請求的操作許可權。如果沒有,將引發SecurityException。
如果想讓應用使用安全管理器和安全性原則,可在啟動JVM時設定-Djava.security.manager選項,還可以同時指定安全性原則檔案。如果在應用中啟用了Java安全管理器,卻沒有指定安全性原則檔案,那麼Java安全管理器將使用預設的安全性原則,它們是由位於目錄$JAVA_HOME/jre/lib/security中的java.policy定義的。
概念策略(Policy) 類裝載器用Policy對象協助它們決定,把一段代碼匯入虛擬機器時應該給它們什麼樣的許可權. 任何時候,每一個應用程式都只有一個Policy對象.策略檔案 Sun的java1.2平台具體的Policy子類採用在一ASCII策略檔案中用上下文無關文法描述安全性原則. 一個策略檔案包括了一系列grant子句,每一個grant子句將一些許可權授給一個代碼來源。保護域(ProtectionDomain) 當類裝載器將類型裝入java虛擬機器時,它們將為每一個類型指派一個保護域,保護域定義了授予 一段特定的代碼的所有許可權.裝載入java虛擬機器的每一個類型都屬於一個且僅屬於一個保護域.存取控制器(AccessController) implies() 判斷一個Permissioin對象的許可權,是否隱含(imply)在另一個Permissioin對象的許可權中。 checkPermission() AccessController的核心方法,這個方法決定一個特定的操作能否被允許. 它自頂向下檢查棧,只要它遇到一個沒有許可權楨,它將拋出一個AccessControlException導常。 doPrivileged() 有的時候,調用棧較上層(更靠近棧頂)的代碼可能希望執行一段代碼,而這段代碼在調用棧的較 下層是不允許執行的。 為了使可信的代碼執行較不可靠的代碼操作(這段不可靠的代碼位於調用棧的較下層且沒有執行 這個操作的許可權),AccessController類重載了四個名為doPrivileged()的靜態方法. AccessController會忽略調用doPrivileged()方法的調用者的調用者的許可權. Permission: 許可權是用抽象類別java.security.Permission的一個子類的執行個體表示的. CodeSource: 代碼來源,包含程式碼程式庫URL和簽名者. Permissions: PermissionCollection(許可權集合)的子類
裝載時產生保護域的步驟:1 根據指定的Policy檔案產生一個Policy對象2 產生CodeSource3 用CodeSource在Policy中找到CodeSource對應的Permissions4 用CodeSource和Permissons構造一個ProtectionDomain5 把ProtectionDomain同這個類在方法區中的類資料聯絡起來(ClassLoader.defineClass()).
運行許可權檢查:如當前應用程式執行new FileInputStream(“a.txt”),java會檢查當前代碼有沒有讀”a.txt”的許可權.步驟:1 調用SecurityManager.checkRead()方法2 調用AccessControl.checkPermission()方法,執行棧檢查
實現安全管理器步驟:
(1) 建立一個SecurityManager的子類;
(2) 覆蓋一些方法。
import java.io.*;
public class TestSecurity
{
public static void main(String args[])
{
try {
System.setSecurityManager(new PasswordSecurityManager("123456"));
} catch (SecurityException se) {
System.out.println("SecurityManager already set!");
}
try {
//DataInputStream fis = new DataInputStream(new FileInputStream("input.txt"));
BufferedReader fis = new BufferedReader(new FileReader("input.txt"));
//DataOutputStream fos = new DataOutputStream( new FileOutputStream("output.txt"));
BufferedWriter fos = new BufferedWriter(new FileWriter("output.txt"));
String inputString;
while ((inputString = fis.readLine()) != null) {
//fos.writeBytes(inputString);
//fos.writeByte('\n');
fos.write(inputString);
fos.write('\n');
}
fis.close();
fos.close();
} catch (IOException ioe) {
System.out.println("I/O failed for SecurityManagerTest.");
}catch(Exception e)
{
System.out.println(e.toString());
}
}
}
import java.io.*;
class PasswordSecurityManager extends SecurityManager {
private String password;
PasswordSecurityManager(String password) {
super();
this.password = password;
}
private boolean accessOK() {
int c;
//DataInputStream dis = new DataInputStream(System.in);
BufferedReader dis = new BufferedReader(new InputStreamReader(System.in));
String response;
System.out.println("What's the secret password?");
try {
response = dis.readLine();
if (response.equals(password))
return true;
else
return false;
} catch (IOException e) {
return false;
}
}
public void checkRead(FileDescriptor filedescriptor) {
if (!accessOK())
throw new SecurityException("Not a Chance!");
}
public void checkRead(String filename) {
if (!accessOK())
throw new SecurityException("No Way!");
}
public void checkRead(String filename, Object executionContext) {
if (!accessOK())
throw new SecurityException("Forget It!");
}
public void checkWrite(FileDescriptor filedescriptor) {
if (!accessOK())
throw new SecurityException("Not!");
}
public void checkWrite(String filename) {
if (!accessOK())
throw new SecurityException("Not Even!");
}
}