麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

實(shí)例分析JVM安全體系:雙親委派、命名空間、保護(hù)域、策略

2019-11-14 15:06:20
字體:
供稿:網(wǎng)友

在了解雙親委派模型之前,先了解一下類加載器的概念:

類加載器的作用就是將真實(shí)的class文件根據(jù)位置將該java類的字節(jié)碼裝入內(nèi)存,并生成對應(yīng)的Class對象。用戶可以通過繼承ClassLoader和重寫findClass方法來定義自己的類加載器進(jìn)行加載,系統(tǒng)類加載器按照層次,分為:
(1).啟動(dòng)類加載器(Bootstrap ClassLoader):將加載 /JAVAHOME/lib以及為-Xbootclasspath所指定的目錄下的類庫,是核心Java API的class文件,用于啟動(dòng)Java虛擬機(jī)
(2).擴(kuò)展類加載器(Extension ClassLoader):將加載/JAVAHOME/lib/ext以及為java.ext.dirs所指定的目錄下的類庫
(3).應(yīng)用程序類加載器(application/System ClassLoader):將加載ClassPath下所指定的類庫,或者稱為類路徑加載器

1.雙親委派
類的加載將使用雙親委派的方式,注意這里的雙親關(guān)系并非通過繼承來實(shí)現(xiàn),而是加載器之間指定或默認(rèn)的委托加載關(guān)系,可以看到在 /java/lang/ClassLoader.java中,通過ClassLoader的構(gòu)造方法顯式指定了其父加載器,而若沒有指定父加載器,那么將 會(huì)把系統(tǒng)類加載器AppClassLoader作為默認(rèn)的父加載器

1
2
3
4
5
6
7
8
9
PRivate ClassLoader(Void unused, ClassLoader parent) {
        this.parent = parent;
        //...
}
 
protected ClassLoader() {
        //getSystemClassLoader()
        this(checkCreateClassLoader(), getSystemClassLoader());
}

加載器對類的加載調(diào)用loadClass()方法實(shí)現(xiàn):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            // 該類沒有被加載
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                    // 先交由父加載器嘗試加載
                        c = parent.loadClass(name, false);
                    } else {
                    // 父加載器為空,即為BootstrapClassLoader,那么查看啟動(dòng)類中是否有該類
                        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.
                    long t1 = System.nanoTime();
                    c = findClass(name);
 
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

可見首先會(huì)檢查該類是否已經(jīng)被加載,若沒有被加載,則會(huì)委托父加載器進(jìn)行裝載,只有當(dāng)父加載器無法加載時(shí),才會(huì)調(diào)用自身的findClass()方法進(jìn)行加載。這樣避免了子加載器加載一些試圖冒名頂替可信任類的不可靠類,也不會(huì)讓子加載器去實(shí)現(xiàn)父加載器實(shí)現(xiàn)的加載工作。
比如某個(gè)用戶自定義的類加載器試圖加載一個(gè)叫做“java.lang.String”的類,那么,該類會(huì)最終委派給啟動(dòng)類加載器 BootstrapClassLoader嘗試加載,那么啟動(dòng)類加載器將加載Java API中的”java.lang.String”類而不會(huì)通過用戶自定義的類加載器去獲得和加載這個(gè)看上去不懷好意的冒名類。

但是僅僅依賴雙親委派是遠(yuǎn)遠(yuǎn)不夠的,假設(shè)這個(gè)用戶自定義的類加載器試圖加載一個(gè)叫做“java.lang.Bomb”的危險(xiǎn)類,而父類加載器無 法加載該類,那么加載工作將由用戶定義的加載器負(fù)責(zé)實(shí)現(xiàn)。由于一個(gè)類的同一包內(nèi)的類(和其子類)可以訪問其protected成員,這個(gè) “java.lang.Bomb”則可能訪問可信任類的一些敏感信息,所以就必須將這個(gè)類與可信任類的訪問域隔離,Java虛擬機(jī)只把這樣彼此訪問的特殊 權(quán)限授予由同一個(gè)類加載器加載的同一包內(nèi)的類型,這樣一個(gè)由同一個(gè)類加載器加載的、屬于同一個(gè)包的多個(gè)類型集合稱為運(yùn)行時(shí)包。

2.命名空間
類加載體系為不同類加載器加載的類提供不同的命名空間,同一命名空間內(nèi)的類可以互相訪問,不同命名空間的類不知道彼此的存在(除非顯式提供訪問機(jī)制)。同一類可以再不同的命名空間內(nèi),但無法在同一命名空間內(nèi)重復(fù)出現(xiàn)。

命名空間是這樣定義的:實(shí)際完成加載類的工作的加載器為定義類加載器,而加載的雙親委托路徑上的所有加載器為初始類加載器,某個(gè)加載器的命名空間就是所有以該加載器為初始類加載器的類所組成。

可以預(yù)見,子加載器的命名空間包括其父/祖先加載器的命名空間和只有自己才可以加載的類所組成。根據(jù)加載體系結(jié)構(gòu)的安全機(jī)制,同一命名空間內(nèi)的類可 以互相訪問,所以父加載器所加載的類不一定可以訪問子加載器所加載的類,但子加載器所加載的類必然可以訪問父加載器加載的類。父加載器加載的類就好像小箱 子,子加載器加載的類可能用到父加載器加載的類,就像一個(gè)大箱子,只能把小箱子放進(jìn)大箱子,而不能反過來做(當(dāng)然顯式的訪問機(jī)制除外)

以自己實(shí)現(xiàn)的類加載器為例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
  package com.ice.classloader;
 
 
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
 
public class MyClassLoader extends ClassLoader {
 
    private String name;    //加載器名稱
    private String path = "E://WorkSpace//ClassLoaderTest//"//加載路徑
    private static final String HOME = "E://WorkSpace//ClassLoaderTest//";
    private final String classFileType = ".class";
 
    public MyClassLoader(String name) {
        super();
        this.name = name;
    }
 
    public MyClassLoader(ClassLoader parent, String name) {
        super(parent);
        this.name = name;
    }
 
    @Override
    public String toString() {
        return this.name;
    }
 
    public String getPath() {
        return path;
    }
 
    public void setPath(String path) {
        this.path = path;
    }
 
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] data = this.loadClassData(name);
        if(data == null)
            throw new ClassNotFoundException();
        return this.defineClass(name, data, 0, data.length);
 
    }
 
    private byte[] loadClassData(String name) {
 
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;
//        System.out.println("  classloader:" + this.name + " try to load");
        try {
            //類名轉(zhuǎn)化為路徑
            name = name.replace(".", "http://");
            is = new FileInputStream(new File(path + name + classFileType));
 
            baos = new ByteArrayOutputStream();
            int ch = 0;
            while (-1 != (ch = is.read())) {
 
                baos.write(ch);
            }
 
            data = baos.toByteArray();
        }
        catch (FileNotFoundException e) {
//            e.printStackTrace();
            return null;
        }
        catch (IOException ioe) {
            ioe.printStackTrace();
      }
        finally {
            try {
                is.close();
                baos.close();
            }
            catch (Exception e2) {
            }
        }
        return data;
    }
 
 
    public static void main(String[] args) throws Exception {
        //假定的系統(tǒng)加載器
        MyClassLoader father = new MyClassLoader("father");
        father.setPath(HOME + "syslib//");
 
        MyClassLoader child = new MyClassLoader(father, "child");
        child.setPath(HOME + "ext//");
 
        MyClassLoader user = new MyClassLoader("user");
        user.setPath(HOME + "usr//");
        System.out.println("-------------test parent--------------");
        //測試父加載器關(guān)系
        traverseParent(child);
        System.out.println("-------------test load begin from child--------------");
        //測試加載
        test(child);
        //測試命名空間
        System.out.println("-------------test namespace--------------");
        testNameSpace(user);
 
    }
 
    public static void traverseParent(ClassLoader loader) throws Exception{
        if(loader == null) return;
        System.out.println("travase classloader:" + loader.toString());
        while(loader.getParent() != null){
            System.out.println(loader.getParent());
            loader = loader.getParent();
        }
    }
 
    public static void test(ClassLoader loader) throws Exception {
        Class<?> clazz = loader.loadClass("com.ice.classloader.LoadedClass");
        Object object = clazz.newInstance();
    }
 
    public static void testNameSpace(ClassLoader loader) throws Exception {
        Class<?> clazz = loader.loadClass("com.ice.classloader.LoadedClass");
        Object object = clazz.newInstance();
        try{
            LoadedClass lc = (LoadedClass) object;
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

被加載類LoadedClass的定義如下:

1
2
3
4
5
6
7
8
9
10
11
12
//被加載類
package com.ice.classloader;
 
public class LoadedClass {
 
    public LoadedClass() {
        System.out.println("LoadedClass is loaded by: "
                + this.getClass().getClassLoader());
 
    }
 
}

(1).雙親委派結(jié)果
child加載器會(huì)委托father進(jìn)行加載,若father的加載目錄下存在著對應(yīng)的class文件,則會(huì)由 父加載器father進(jìn)行對應(yīng)的加載工作(father也會(huì)交由AppClassLoader和ExtClassLoader嘗試進(jìn)行加載,但這兩個(gè)加載 器并不知道如何加載,故而最后會(huì)自己嘗試進(jìn)行加載)

當(dāng)father的加載目錄下沒有對應(yīng)的class文件,則會(huì)交由child進(jìn)行加載

(2).命名空間隔離
由于MyClassLoader是通過系統(tǒng)的(應(yīng)用程序類加載器/類路徑加載器加載的),而 LoadedClass是由user加載器所加載的,AppClassLoader加載器是user加載器的父加載器,故由父加載器加載的類 MyClassLoader無法看見子加載器user所加載的LoadedClass類,在MyClassLoader中嘗試實(shí)例化 LoadedClass類時(shí)就會(huì)出現(xiàn)如下錯(cuò)誤:

對應(yīng)出錯(cuò)的正是嘗試實(shí)例化LoadedClass類的那一行

try{    LoadedClass lc = (LoadedClass) object;}catch(Exception e){

(3).運(yùn)行時(shí)包
當(dāng)請求加載一個(gè)com.ice.classloader.virus類時(shí),AppClassLoader路徑下沒有 該類的class文件,那么attaker加載器將會(huì)加載這個(gè)virus類,并暗示其為com.ice.classloader的一部分,該類想要獲取 com.ice.classloader包下被信任類的訪問權(quán)限。但由于權(quán)限檢查時(shí),由于該Virus類由attacker加載而非 AppClassLoader加載,故對MyClassLoader受保護(hù)成員的訪問將會(huì)被阻止。

1
2
3
4
5
6
7
8
9
10
11
12
package com.ice.classloader;
 
public class Virus {
 
    public Virus() {
        System.out.println("Virus is loaded by: "
                + this.getClass().getClassLoader());
        MyClassLoader cl = (MyClassLoader) this.getClass().getClassLoader();
        System.out.println("secret is:" + cl.secret);
    }
 
}

MyClassLoader 由AppClassLoader所加載,而Virus由用戶自定義的加載器attacker所加載,雖然AppClassLoader是attacker 的父加載器,即MyClassLoader對Virus可見,但由于兩者不是由同一個(gè)加載器所加載,即不屬于同一個(gè)運(yùn)行時(shí)包,那么Virus對 MyClassLoader的受保護(hù)成員訪問受限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MyClassLoader extends ClassLoader {
    protected int secret = -1;
//...
    public static void main(String[] args) throws Exception {
            //其父加載器為Bootstrap ClassLoader
            MyClassLoader loader = new MyClassLoader(null, "loader");
            loader.setPath(HOME + "usr//");
 
            MyClassLoader attacker = new MyClassLoader("attacker");
            attacker.setPath(HOME + "attacker//");
 
            System.out.println("MyClassLoader's classloader:" + MyClassLoader.class.getClassLoader());
 
            System.out.println("-------------test parent--------------");
            //測試父加載器關(guān)系
            traverseParent(attacker);
 
            System.out.println("-------------test in-package access--------------");
            testVirus(attacker);
 
        }
 
        public static void traverseParent(ClassLoader loader) throws Exception{
            if(loader == null) return;
            System.out.println("travase classloader:" + loader.toString());
            while(loader.getParent() != null){
                System.out.println(loader.getParent());
                loader = loader.getParent();
            }
        }
 
 
        public static void testVirus(ClassLoader loader) throws Exception {
            Class<?> clazz = loader.loadClass("com.ice.classloader.Virus");
            Object object = clazz.newInstance();
        }
    }

結(jié)果如下:

注意命名空間的隔離與運(yùn)行時(shí)包隔離的區(qū)別,不同命名空間的類之間不可見,而同一命名空間內(nèi)的類可能由不同的加載器進(jìn)行加載,如啟動(dòng)類加載器加載 的核心JavaAPI和用戶自定義加載器加載的類,這些類及時(shí)聲明定義為同一個(gè)包,但是由于不是由同一個(gè)加載器加載的,即不屬于同一個(gè)運(yùn)行時(shí)包,那么不同 運(yùn)行時(shí)包內(nèi)的類之間就存在對包可見成員的訪問限制。

3.策略與保護(hù)域
除了命名空間的訪問隔離和雙親委派的受信類保護(hù),類加載器體系還是用保護(hù)域來定義代碼在運(yùn)行時(shí)可以獲得的權(quán)限。同樣在分析保護(hù)域之前,先了解類Java虛擬機(jī)的安全訪問控制及策略。

Java的沙箱模型可以由用戶自定義,這是通過用戶定制沙箱的安全管理器(SecurityManager)來定義沙箱的安全邊界,以為程序運(yùn) 行指定用戶自定義的安全策略和訪問控制。應(yīng)用程序通過 System.setSecurityManager()/“-Djava.security.manager”來指定/啟動(dòng)安全管理器,每當(dāng) JavaAPI執(zhí)行一些可能不安全的操作時(shí),如對文件的讀寫和刪除等,就會(huì)向安全管理器進(jìn)行權(quán)限檢查,若權(quán)限檢查不通過,將會(huì)拋出一個(gè)安全異常,若權(quán)限檢 查通過,則允許該操作的執(zhí)行。

比如創(chuàng)建一個(gè)FileInputStream時(shí),會(huì)調(diào)用SecurityManager的checkRead()進(jìn)行讀取權(quán)限的檢查:

1
2
3
4
5
6
7
8
9
10
11
12
13
public FileInputStream(File file) throws FileNotFoundException {
        String name = (file != null ? file.getPath() : null);
        SecurityManager security = System.getSecurityManager();
        if (security != null) {
            security.checkRead(name);
        }
        if (name == null) {
            throw new NullPointerException();
        }
        fd = new FileDescriptor();
        fd.incrementAndGetUseCount();
        open(name);
    }

checkRead()即以讀動(dòng)作的FilePermission為參數(shù)調(diào)用checkPermission()

1
2
3
4
public void checkRead(String file) {
        checkPermission(new FilePermission(file,
            SecurityConstants.FILE_READ_ACTION));
    }

jdk1.2版本后,可以使用checkPermission(Permission perm)和checkPermission(Permission perm, Object context)來進(jìn)行權(quán)限檢查,其中perm為請求執(zhí)行操作所需要的權(quán)限,如java.io.FilePermission對“/usr /indata.txt”請求“read”操作。checkPermission()實(shí)際上在對當(dāng)前線程的方法棧進(jìn)行優(yōu)化后,獲得一個(gè)訪問控制環(huán)境 AccessControlContext,并調(diào)用其checkPermission()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void checkPermission(Permission perm)
                 throws AccessControlException
    {
        //System.err.println("checkPermission "+perm);
        //Thread.currentThread().dumpStack();
 
        if (perm == null) {
            throw new NullPointerException("permission can't be null");
        }
 
        AccessControlContext stack = getStackAccessControlContext();
        // if context is null, we had privileged system code on the stack.
        if (stack == null) {
           //...debug相關(guān)
            return;
        }
 
        AccessControlContext acc = stack.optimize();
        acc.checkPermission(perm);
    }

checkPermission()會(huì)從方法的棧頂向棧底遍歷(檢查方法所在類的保護(hù)域權(quán)限,context是一個(gè) ProtectionDomain數(shù)組),當(dāng)遇到一個(gè)沒有權(quán)限的棧幀就會(huì)拋出一個(gè)AccessControlException。即對于一次需要進(jìn)行權(quán)限 檢查的訪問,對于該訪問的方法的每一個(gè)調(diào)用層次都必須具有對應(yīng)的訪問權(quán)限。
對權(quán)限的判定是通過implies()來進(jìn)行的,implies()在Permission類、PermissionCollection、ProtectionDomain類中聲明。在Permission類(具體實(shí)現(xiàn)的子類)中,該方法將確定由該P(yáng)ermission所代表的對象,是否隱含了將要判斷的Permission對象的權(quán)限中, 如對”/test/*”目錄的讀寫權(quán)限testAllPermission,隱含了對”/test/test.txt”文件的讀寫權(quán)限 testFilePermission,即testAllPermission.implies(testFilePermission) 的值為true,反之為false。 在ProtectionDomain(其PermissionCollection)中,將進(jìn)行權(quán)限集合內(nèi) implies()的判定,實(shí)際上就是在PermissionCollection中遍歷保護(hù)域所擁有的權(quán)限,調(diào)用implies()判定其是否具有對應(yīng)的訪問權(quán)限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void checkPermission(Permission perm)
        throws AccessControlException
    {
       //...
        if (context == null)
            return;
 
        for (int i=0; i< context.length; i++) {
            if (context[i] != null &&  !context[i].implies(perm)) {
                //...
                throw new AccessControlException("access denied "+perm, perm);
            }
        }
 
        // ...
 
        return;
    }

那么,類的訪問權(quán)限(保護(hù)域)是如何指定的?
(1).類與訪問權(quán)限是什么?
每個(gè)class文件均和一個(gè)代碼來源相關(guān)聯(lián),這個(gè)代碼來源(java.security.CodeSource)通過URL類成員location指向代碼庫和對該class文件進(jìn)行簽名的零個(gè)或多個(gè)證書對象的數(shù)組(class文件在進(jìn)行代碼認(rèn)證的過程中可能經(jīng)過多個(gè)證書簽名,也可能沒有進(jìn)行簽名) 。
訪問控制策略Policy對權(quán)限的授予是以CodeSource為基礎(chǔ)進(jìn)行的,每個(gè)CodeSource擁有若干個(gè)Permission,這些Permission對象會(huì)被具體地以其子類,如FilePermission、SocketPermission等描述,并且和CodeSource相關(guān)聯(lián)的Permission對象將被封裝在java.security.PermissionCollection(抽象類)的一個(gè)子類實(shí)例中,以描述該CodeSource所獲取的權(quán)限。
(2).從類的加載到保護(hù)域探尋類訪問權(quán)限的指定:
加載器會(huì)調(diào)用defineClass解析和加載類的Class實(shí)例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
                                         ProtectionDomain protectionDomain)
        throws ClassFormatError
    {
        protectionDomain = preDefineClass(name, protectionDomain);
 
        Class c = null;
        String source = defineClassSourceLocation(protectionDomain);
 
        try {
            c = defineClass1(name, b, off, len, protectionDomain, source);
        } catch (ClassFormatError cfe) {
            c = defineTransformedClass(name, b, off, len, protectionDomain, cfe,
                                       source);
        }
 
        postDefineClass(c, protectionDomain);
        return c;
    }

在defineClass()中,會(huì)調(diào)用preDefineClass()獲取ProtectionDomain:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private ProtectionDomain preDefineClass(String name,
                                            ProtectionDomain pd)
    {
        if (!checkName(name))
            throw new NoClassDefFoundError("IllegalName: " + name);
 
        if ((name != null) && name.startsWith("java.")) {
            throw new SecurityException
                ("Prohibited package name: " +
                 name.substring(0, name.lastIndexOf('.')));
        }
        if (pd == null) {
            pd = defaultDomain;
        }
 
        if (name != null) checkCerts(name, pd.getCodeSource());
 
        return pd;
    }

當(dāng)沒有指定保護(hù)域時(shí),就會(huì)為其指定一個(gè)空的保護(hù)域,若指定了保護(hù)域則使用加載器所指定的保護(hù)域。

類加載器的實(shí)現(xiàn)可以通過將代碼來源(CodeSource),即代碼庫和該class文件的所有簽名者信息,傳遞給當(dāng)前的 Policy對象的getPermissions()方法,來查詢該代碼來源所擁有的權(quán)限集合PermissionCollection(在策略初始化時(shí) 生成),并以此構(gòu)造一個(gè)保護(hù)域傳遞給defineClass(),以此指定類的保護(hù)域。

(3).Java應(yīng)用程序訪問控制策略是由抽象類java.security.Policy的子類實(shí)例所描述的,通過設(shè)置 policy.provider屬性值來指定Policy的實(shí)現(xiàn)類,該屬性值定義在/jre/lib/security/java.security文件 中

1
2
3
4
5
#
# Class to instantiate as the system Policy. This is the name of the class
# that will be used as the Policy object.
#
policy.provider=sun.security.provider.PolicyFile

可見默認(rèn)是使用PolicyFile類來實(shí)現(xiàn)訪問控制策略,該類將使用從策略文件中讀取并解析訪問控制策略的方式形成策略。
也可以通過實(shí)現(xiàn)自己的Policy并調(diào)用Policy的setPolicy()方法來替換當(dāng)前Policy對象。

對Java應(yīng)用程序的訪問控制策略是由抽象類java.security.Policy的子類實(shí)例實(shí)現(xiàn)的,其實(shí)現(xiàn)方式可以采用很多種方法,如從一個(gè) 結(jié)構(gòu)化ASCII文件中讀取,從一個(gè)Policy的二進(jìn)制class文件中讀取,從一個(gè)數(shù)據(jù)庫中讀取,PolicyFile就是使用了從ASCII策略文 件中讀取的方法,策略文件定義在/jre/lib/security/java.security中:

1
2
3
4
1 # The default is to have a single system-wide policy file,
2 # and a policy file in the user's home directory.
3 policy.url.1=file:${java.home}/lib/security/java.policy
4 policy.url.2=file:${user.home}/.java.policy

可以在java.security文件中修改或添加policy.url.x來指定用戶自己想要的策略,也可以在運(yùn)行時(shí)使用”-Djava.security.policy”命令行參數(shù)進(jìn)行設(shè)置,如:
-Djava.security.manager -Djava.security.policy = mypolicy.txt
其中如果沒有指定java.security.manager,那么應(yīng)用程序就不會(huì)安裝任何的安全管理器,而代碼也就沒有任何權(quán)限限制。mypolicy.txt就是用戶自頂一個(gè)策略文件,這里使用的是相對路徑,將使用程序的啟動(dòng)目錄

以/jre/lib/security/java.policy為例說明策略文件,在該文件中使用上下文無關(guān)文法描述安全策略
如:

1
2
3
grant codeBase "file:${{java.ext.dirs}}/*" {
    permission java.security.AllPermission;
};

policy文件的基本語法如下:

1
2
3
4
5
6
7
8
9
keystore "keystore_url",
"keystore_type";
 
grant [SignedBy "signer_names"] [, CodeBase "URL"] [,principal principal_class_name "principal_name",]{
Permission permission_class_name
[ "target_name" ]
[, "action"] [, SignedBy "signer_names"];
Permission ...
};
  • keystore:
    keystore_url指定了簽名者的公鑰的證書文件所在位置,可以使相對URL,如keystore “mykey”,這個(gè)相對路徑指向了程序使用該策略文件的啟動(dòng)目錄,比如,該策略文件由policy.url.x指定在”e://security /policy/mypolicy.txt”,那么公鑰證書就在”e://security/policy/mykey”文件中。當(dāng)然也可以使用絕對路徑 指定公鑰路徑。
    keystore_type指定了密鑰倉庫信息的存儲(chǔ)和數(shù)據(jù)格式,也定義了保護(hù)密鑰倉庫中私鑰和密鑰倉庫完整性的算法,默認(rèn)將使”JKS”類型
  • grant子句:
    授予 指定類型(代碼) 指定權(quán)限
    其中對代碼類型的描述有兩種:
    signedBy表示簽名者別名,可以是由”,”分隔的若干個(gè)簽名者
    codeBase表示一個(gè)特定的加載位置,從該目錄下加載的代碼都將被賦予特定的權(quán)限
  • permission:
    permission由權(quán)限類型、操作目標(biāo)、操作動(dòng)作三部分組成,如
    permission java.io.FilePermission “note.txt” “read”即為對程序啟動(dòng)目錄下(相對路徑)的note.txt的讀取權(quán)限

最后以深入jvm(第二版)一書中的例子來介紹策略文件的使用以及保護(hù)域的作用:

Doer接口:

1
2
3
4
5
6
// /com/ice/security/doer/Doer.java
package com.ice.security.doer;
 
public interface Doer {
    void doYourThing();
}

Doer的實(shí)現(xiàn)類Friend,由Friend所簽名,將作為受信認(rèn)類訪問“friend.txt”和“stranger.txt”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// /com/ice/security/friend/Friend.java
package com.ice.security.friend;
 
import java.security.AccessController;
import java.security.PrivilegedAction;
 
import com.ice.security.doer.Doer;
 
public class Friend implements Doer{
    private Doer next;
    private boolean direct;
 
    public Friend(Doer next, boolean direct){
        this.next = next;
        this.direct = direct;
    }
 
    @Override
    public void doYourThing() {
        if(direct){
            next.doYourThing();
        }else{
            AccessController.doPrivileged(
                    new PrivilegedAction() {
                        public Object run(){
                            next.doYourThing();
                            return null;
                        }
                    }
            );
        }
    }
 
}

Doer的實(shí)現(xiàn)類Stranger,由Stranger所簽名,作為不受信認(rèn)類,僅能訪問“stranger.txt”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//com/ice/security/stranger/Stranger.java
package com.ice.security.stranger;
 
import java.security.AccessController;
import java.security.PrivilegedAction;
 
import com.ice.security.doer.Doer;
 
public class Stranger implements Doer{
    private Doer next;
    private boolean direct;
 
    public Stranger(Doer next, boolean direct){
        this.next = next;
        this.direct = direct;
    }
 
    @Override
    public void doYourThing() {
        if(direct){
            next.doYourThing();
        }else{
            AccessController.doPrivileged(
                    new PrivilegedAction() {
                        public Object run(){
                            next.doYourThing();
                            return null;
                        }
                    }
            );
        }
    }
 
}

txt文件的顯示輸出類TextFileDisplayer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//TextFileDisplayer.java
import java.io.CharArrayWriter;
import java.io.FileReader;
import java.io.IOException;
 
import com.ice.security.doer.Doer;
 
 
public class TextFileDisplayer implements Doer{
    private String fileName;
    public TextFileDisplayer(String fileName){
        this.fileName = fileName;
    }
 
    @Override
    public void doYourThing() {
            try{
                FileReader fr = new FileReader(fileName);
                try {
                    CharArrayWriter caw = new CharArrayWriter();
                    int c;
                    while((c = fr.read()) != -1){
                        caw.write(c);
                    }
                    System.out.println(caw.toString());
                } catch (IOException e) {
 
                }finally{
                    try{
                        fr.close();
                    }catch (IOException e){
 
                    }
                }
            }catch (IOException e) {
 
            }
    }
 
}

1.將Friend和Stranger分別導(dǎo)出為jar文件,放在指定目錄(這里放在”E:/java/security”目錄下)以待不同的機(jī)構(gòu) 進(jìn)行簽名,F(xiàn)riend所在包假定為比較有信用的機(jī)構(gòu)”friend”進(jìn)行簽名,而Stranger所在包假定為一個(gè)不受信任的機(jī)構(gòu)”stranger” 進(jìn)行簽名。
(1).調(diào)用命令 jar cvf xxx.jar <_ClassPath> 進(jìn)行打包
(注意打包后,若沒有把jar包放在單獨(dú)的目錄下,需要?jiǎng)h除原java文件編譯產(chǎn)生的class文件,以免程序運(yùn)行直接加載目錄下class文件而非包內(nèi)的class文件)
這里分別調(diào)用
jar cvf friend.jar com/ice/security/friend/*.class 將friend包內(nèi)的class文件打包

jar cvf stranger.jar com/ice/security/stranger/*.class 將friend包內(nèi)的class文件打包

(2).使用keytool可以用來生成新的密鑰對,并與一個(gè)別名關(guān)聯(lián),用密碼加以保護(hù)存放在keystore文件中
使用keytool -genkey -alias friend -keypass 123456 -validity 10000 -keystore mykey 命令:

該密鑰的別名是friend,別名密碼是123456(至少6位),有效期是10000天,存放在一個(gè)mykey的keystore文件中,keystore密碼為myfriendkey

類似地,生成一個(gè)別名stranger的密鑰對

為了方便起見,兩個(gè)不同的簽名者stranger和friend的密鑰均存放在mykey中,mykey的訪問密碼是myfriendkey,密鑰的訪問密碼都是123456

可以看到在目錄下生成了一個(gè)mykey文件
(3).使用jarsigner -keystore -storepass -keypass 命令進(jìn)行簽名
這里:
jarsigner -keystore mykey -storepass myfriendkey -keypass 123456 friend.jar friend
jarsigner -keystore mykey -storepass myfriendkey -keypass 123456 stranger.jar stranger
使用friend密鑰對friend.jar進(jìn)行簽名,使用stranger密鑰對stranger.jar進(jìn)行簽名

(4).最后可以使用
keytool -export -alias -storepass -file -keystore
這里分別用:
keytool -export -alias friend -storepass myfriendkey -file friend.cer -keystore mykey
keytool -export -alias stranger -storepass myfriendkey -file stranger.cer -keystore mykey
導(dǎo)出friend和stranger的發(fā)行證書

 

2.編寫自己的策略文件,放在當(dāng)前目錄下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//mypolicy.txt
keystore "mykey";
 
grant signedBy "friend"{
    permission java.io.FilePermission "friend.txt","read";
    permission java.io.FilePermission "stranger.txt","read";
};
 
grant signedBy "stranger"{
    permission java.io.FilePermission "stranger.txt","read";
};
 
grant codeBase "file:${com.ice.home}/com*"{
    permission java.io.FilePermission "friend.txt","read";
    permission java.io.FilePermission "stranger.txt","read";
};

這里friend簽名的類和${com.ice.home}.com(后面設(shè)置 為”e:/java/security/com”,存放著Doer接口的class文件)可以讀取”friend.txt” 和”stranger.txt”,而stranger簽名的類只能讀取”stranger.txt”
(這里為了方便,直接使用mykey而非發(fā)布的證書)
(1).添加Doer接口類的class文件(對應(yīng)路徑)和friend.txt和stranger.txt兩個(gè)測試文件
(2).通過權(quán)限檢查的例子:

1
2
3
4
5
6
7
8
public class ProtectionDomainTest {
    public static void main(String[] args){
    TextFileDisplayer tfd = new TextFileDisplayer("stranger.txt");
    Friend friend = new Friend(tfd, true);
    Stranger stranger = new Stranger(friend, true);
    stranger.doYourThing();
    }
}

調(diào)用java -Djava.security.manager -Djava.security.policy=mypolicy.txt -Dcom.ice.home=e:/java/security -cp .;friend.jar;stranger.jar ProtectionDomainTest測試運(yùn)行,其中指定了com.ice.home的路徑,通過-cp設(shè)置了類路徑

(3).不能通過權(quán)限檢查的例子:

1
2
3
4
5
6
7
8
public class ProtectionDomainTest {
    public static void main(String[] args){
    TextFileDisplayer tfd = new TextFileDisplayer("friend.txt");
    Friend friend = new Friend(tfd, true);
    Stranger stranger = new Stranger(friend, true);
    stranger.doYourThing();
    }
}

與(2)類似,但stranger會(huì)嘗試讓friend讀取”friend.txt”,這會(huì)被阻止

全能程序員交流QQ群290551701,聚集很多互聯(lián)網(wǎng)精英,技術(shù)總監(jiān),架構(gòu)師,項(xiàng)目經(jīng)理!開源技術(shù)研究,歡迎業(yè)內(nèi)人士,大牛及新手有志于從事IT行業(yè)人員進(jìn)入!


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 国产精品成人一区二区三区吃奶 | 国产88久久久国产精品免费二区 | av懂色| 操碰网 | 亚洲精品午夜国产va久久成人 | 全黄毛片| 久久综合艹 | 香蕉久久久精品 | 久久久久久久久久美女 | 国产精品无码久久久久 | 亚洲成人午夜精品 | 国内精品久久久久久久久久 | 国产午夜三级一区二区三桃花影视 | 色玖玖综合 | 久久精品一二三区 | 国产精品久久久久无码av | 成人男女免费视频 | 污视频在线看 | 成人免费毛片片v | 中文日产幕无线码6区免费版 | 国产一区二区三区四区波多野结衣 | 亚洲人成网站在e线播放 | 欧洲精品久久 | 91精品国产91久久久久久吃药 | 嗯啊羞羞视频 | 热99在线视频 | 热re91久久精品国产99热 | 欧美日韩精品不卡一区二区三区 | 一级黄色av电影 | av免费在线观看av | 国产精品久久久久久久久久大牛 | 日韩黄在线 | 天天碰天天操 | 国产精品久久国产精品 | av在线看网站 | 欧美日韩在线看片 | 日本欧美一区二区 | 午夜电影视频 | a免费视频| 毛片免费在线观看 | 看全色黄大色黄大片女图片 |