六,存取控制器

来源:互联网 发布:家庭网络布线交换机 编辑:程序博客网 时间:2024/06/05 17:26

安全管理器大部分功能都是在存取控制器基础上实现的,可以说存取控制器是可以取代安全管理器的。


一般来说安全管理器是调用存取控制器的功能!


前面章节介绍的沙箱就是说的存取控制器,存取控制器就是沙箱的实现。回顾一下沙箱的基本要素,这些都是建立存取控制的基础

基本要素:

1,代码源

2,权限

3,策略:管理员可以设置策略文件,策略文件上面描述了所有的权限;在安全管理器中,策略对象表示为对所有权限的封装(内存)

4,保护域:利于权限的检索,指对某代码源相应权限的封装


我们先详细解读每一个要素的实现,然后再解释存取控制器是如何工作的


一,CodeSource类(java.security.CodeSource)  —————— 代码源

主要方法:

http://www.yq1012.com/api/java/security/CodeSource.html


二,权限类, Permission类

http://www.yq1012.com/api/java/security/Permission.html

1,权限的三个特性:类型、名、操作


注意:权限是类型的一个实例

如:在策略文件中定义一个权限对象

permission java.util.PropertyPermission "java.version", "read";

上面描述的权限在内存中的表示为:permission p = new java.util.PropertyPermission("java.version", "read");

可以看出对象是java.util.PropertyPermission类型的一个实例,构造时带名和操作参数。


对于我们这些普通java开发者,很少定义权限类,但是我们可以自定义权限类,只不过在实现”名、操作“的通配匹配上算法有点复杂,

另外,BasicPermission类提供了层次式通配匹配操作,我们可以直接继承或依赖他,也可以只继承Permission类来自行完成需要的匹配算法。


以下是参考书籍上的一个例子,

----此例子的名只是一个单值,并不是层次的,所以不需要复杂的匹配,操作只有2个,view和update,操作配置算法用或操作就完成,很简单

----如果名是层次的,要写路径覆盖算法,代码太多,这里只是简单的说明我们如何编写自己的权限类,

----注意equals(Object o),implies(Permission p) ,getActions() 核心方法的实现细节,他们都是抽象的,要自己实现的匹配逻辑

import java.security.*;import java.util.*;public class XYZPayrollPermission extends Permission {    protected int mask;    static private int VIEW = 0x01;    static private int UPDATE = 0x02;    public XYZPayrollPermission(String name) {        // Our permission must always have an action, so we        // choose a default one here.        this(name, "view");    }    public XYZPayrollPermission(String name, String action) {        // Our superclass, however, does not support actions        // so we don't provide one to that.        super(name);        parse(action);    }    private void parse(String action) {        // Look in the action string for the words view and        // update, separated by white space or by a comma        StringTokenizer st = new StringTokenizer(action, ",\t ");        mask = 0;        while (st.hasMoreTokens()) {            String tok = st.nextToken();            if (tok.equals("view"))                mask |= VIEW;            else if (tok.equals("update"))                mask |= UPDATE;            else throw new IllegalArgumentException(                                    "Unknown action " + tok);        }    }    public boolean implies(Permission permission) {        if (!(permission instanceof XYZPayrollPermission))            return false;                XYZPayrollPermission p = (XYZPayrollPermission) permission;        String name = getName();        // The name must be either the wildcard *, which signifies        // all possible names, or the name must match our name        if (!name.equals("*") && !name.equals(p.getName()))            return false;        // Similarly, the requested actions must all match actions        // that we've been constructed with.        if ((mask & p.mask) != p.mask)            return false;        // Only if both the action and name match do we return true.        return true;    }    public boolean equals(Object o) {        if (!(o instanceof XYZPayrollPermission))            return false;                // For equality, we check the name and action mask.        // We must provide a method definition like this, since        // the security system expects us to do a deep check for        // equality rather than relying on object reference        // equality.        XYZPayrollPermission p = (XYZPayrollPermission) o;        return ((p.getName().equals(getName())) && (p.mask == mask));    }    public int hashCode() {        // We must always provide a hash code for permissions,        // because the hashes must match if the permissions compare        // as equals. The default implementation of this method        // wouldn't provide that.        return getName().hashCode() ^ mask;    }    public String getActions() {        // This method must return the same string, no matter how        // the action list was passed to the constructor.        if (mask == 0)            return "";        else if (mask == VIEW)            return "view";        else if (mask == UPDATE)            return "update";        else if (mask == (VIEW | UPDATE))            return "view, update";        else throw new IllegalArgumentException("Unknown mask");    }    public PermissionCollection newPermissionsCollection() {        // More about this in a later example.        return new XYZPayrollPermissionCollection();    }    public static void main(String[] args) {        XYZPayrollPermission p1 = new XYZPayrollPermission("sdo", "view");        XYZPayrollPermission p2 = new XYZPayrollPermission(args[0], args[1]);        System.out.println("P1 is " + p1);        System.out.println("P2 is " + p2);        System.out.println("P1 -> P2 is " + p1.implies(p2));        System.out.println("P2 -> P1 is " + p2.implies(p1));    }}


2,权限集(抽线类):PermissionCollection类,某一个权限类的所有实例的集合,用途:1-存储权限;2-检查是否包含指定权限

3,权限集集合:Permissions类,他继承权限集PermissionCollection,和权限集表现一样的接口,因此作用一样,只不过他更强,他可以存储多个不同类型的权限集,也可用于不同类型权限的检索


以下是参考书籍上的一个例子,

import java.util.*;import java.security.*;import java.util.*;public class XYZPayrollPermissionCollection extends PermissionCollection {    private Hashtable permissions;    // We keep track of whether the * name has been added to make    // the implies method simpler.    private boolean addedAdmin;    private int adminMask;    XYZPayrollPermissionCollection() {        permissions = new Hashtable();        addedAdmin = false;    }    public void add(Permission p) {        // Required test        if (isReadOnly())            throw new IllegalArgumentException("Read only collection");        // This is a homogenous collection, as are all         // PermissionCollections that you'll implement.        if (!(p instanceof XYZPayrollPermission))            throw new IllegalArgumentException("Wrong permission type");        XYZPayrollPermission xyz = (XYZPayrollPermission) p;        String name = xyz.getName();        XYZPayrollPermission other =                        (XYZPayrollPermission) permissions.get(name);        if (other != null)            xyz = merge(xyz, other);        // An administrative permission. The administrative permission        // may have only view or only update or both, and multiple        // admin permissions may be added, so the masks are OR-ed        // together.        if (name.equals("*")) {            addedAdmin = true;            adminMask = xyz.mask | adminMask;        }        permissions.put(name, xyz);    }    public Enumeration elements() {        return permissions.elements();    }    public boolean implies(Permission p) {        if (!(p instanceof XYZPayrollPermission))            return false;        XYZPayrollPermission xyz = (XYZPayrollPermission) p;        // If the admin name is present, then all names are implied;        // we need check only the permissions.        if (addedAdmin && (adminMask & xyz.mask) == xyz.mask)            return true;        // Otherwise, we can just see if the given individual is        // in our table and if so, see if the permissions match.        Permission inTable = (Permission) permissions.get(xyz.getName());        if (inTable == null)            return false;        return inTable.implies(xyz);    }    // This is called when the same name is added twice to the    // permissions; we merge the action lists and only store the    // name once.    private XYZPayrollPermission merge(XYZPayrollPermission a,                                       XYZPayrollPermission b) {        String aAction = a.getActions();        if (aAction.equals(""))            return b;        String bAction = b.getActions();        if (bAction.equals(""))            return a;        return new XYZPayrollPermission(a.getName(), aAction + "," + bAction);    }}

三,Policy类  策略


权限集要和代码源相关联,之后我们才能判断出代码具有什么样的权限。策略类定义了这种关联。

如果要自己写策略,

----就需要解读策略文件,或者权限存于数据库,你需要自己的一套解读方式

----需要为代码源关联权限集,注意权限集的创建并不是由策略完成的,策略只是保存数据,创建权限的工作由运行时的类加载器搞定

----策略类需要读文件或网络的权限,往往是特权,特权在本文后面介绍


其实我在读完存取控制后,就开始担心java安全策略的效率,但等到读完类加载器后,这些顾虑都没有了,策略对象里面代码源的权限集并不是启动java的时候就生成的,而是由类加载器实时完成的。

所以我们并不用担心JAVA虚拟机安全的处理速度,更不用担心检索速度(内存速度)


例子:

例子展示了策略对象的主要功能

----存储权限 (具体的权限由类加载器生成,然后抛给策略对象的)

----检索代码基的权限集

import java.security.*;import java.util.*;public class MyPolicy extends Policy {    // This inner class defines a simple set of permissions:    // either everything is allowed (the implies() method always    // returns true, and the collection contains an AllPermission    // object) or everything is prohibited (the implies()    // method always returns false and the collection is empty).    static class SimplePermissions extends PermissionCollection {        boolean allow;        Permissions perms;        SimplePermissions(boolean b) {            allow = b;            perms = new Permissions();            if (allow)                perms.add(new AllPermission());        }        public void add(Permission p) {            if (isReadOnly())                throw new SecurityException(                             "Can't add to this collection");            perms.add(p);        }        public Enumeration elements() {            return perms.elements();        }        public boolean implies(Permission p) {            return allow;        }    }    // We never change the policy    public void refresh() {}    //If the code was loaded from a file, return a collection    // that implies all permissions. Otherwise, return an    // empty collection (one that implies no permissions).    public PermissionCollection getPermissions(CodeSource cs) {        if (cs.getLocation().getProtocol().equals("file"))            return new SimplePermissions(true);        return new SimplePermissions(false);    }}

在虚拟机中,任何时候只有一个策略对象,

我们可以替换系统的策略类,通过程序化或者管理手段配置

程序化: setPolicy(Policy p)

管理手段:

---java.security中 plicy.provider=sun.security.provider.PolicyFile 替换为自己的类

---把自己的类放如rt.jar中,或者在运行虚拟机时正确适合参数以指定引导路径 java -Xbootclasspath:/files/policyfile  ...


四,保护域 ProtectionDomain

前面谈过Permissions对象——权限集集和,他和某一个代码基关联起来就是保护域

也就是说,他存储了某个代码基的所有权限集集和

具体核心方法请参考API文档 


保护域对开发人员是透明的,我们不需要了解他的API细节,

但我们要知道,每一个java类都属于且仅属于一个保护域,定义类时,保护域可以来自默认的安全模型(策略文件),也可由类加载器拓展(或替换)。



 

五、存取控制器  AccessController


前面介绍了存取控制器的要素,存取控制器没有实例,构造函数私有,有一组静态方法用于确定是否得到权限以实现某个操作


例子演示了他简单的用法

<span style="font-size:12px;">import java.applet.*;import java.net.*;import java.security.*;public class AccessTest extends Applet {    public void init() {        SocketPermission sp = new SocketPermission(                            getParameter("host") + ":6000", "connect");        try {            AccessController.checkPermission(sp);            System.out.println("Ok to open socket");        } catch (AccessControlException ace) {            System.out.println(ace);        }    }}</span>

1,存取控制是如何工作的——如何检索权限的

----存取控制器方法调用后,存取控制开始工作

----存取控制器检索堆栈中最后压入栈的类,存取控制类除外,检索类代码基是否有相应权限,以上代码是检索是否有套接字权限,host:6000

----有权限,就检测堆栈前一个类,直到堆栈完毕。

----没有权限,就抛出运行时异常,线程完毕。


来自于不同加载器和不同签名jar包的类,由加载器设置权限集合,所以类加载器为了安全考虑,必须首先检索父加载器是否加载了类,


这里不能画图 唉!


2,特权接口,PrivilegedAction 和 PrivilegedExceptionAction 接口

实现特权接口的类,就是特权类,只有一个方法就是run(); 


如果我们把一段程序封装在特权类中,我们就能让存取控制器网开一面,阻止存取控制递归检索堆栈中的权限

话题:A类有套接字locahost:6000权限,B类没有,如果B调用A,那么网络访问就会失败,但这种情况下,我们希望A能顺利访问网络,怎么办呢?

解答:特权类

           我们改造类A,让A访问网络的代码封装在特权类中,这样不管调用A的类是否具备套接字权限,A的对象都能顺利访问网络


<span style="font-size:12px;">import java.net.*;import java.io.*;import java.security.*;public class A {    public A() {        try {            // This class is used by the doPrivilged() method to            // open a socket            class doSocket implements PrivilegedExceptionAction {                public Object run() throws UnknownHostException,                                     IOException {                    return new Socket("localhost", 6000);                }            };            doSocket ds = new doSocket();            Socket s = (Socket) AccessController.doPrivileged(ds);        } catch (PrivilegedActionException pae) {            Exception e = pae.getException();            if (e instanceof UnknownHostException) {                // process host exception            }            else if (e instanceof IOException) {                // process IOException            }            else {                // e must be a runtime exception                throw (RuntimeException) e;            }        }    }}</span>

我们看看例子堆栈的情况

-----new Socket("localhost",6000) 会在内部调用存取控制器, 存取控制器会递归堆栈检索指定权限 :new java.net.SocketPermission("localhost",6000)

-----存取控制先检索类核心API  Socket 是否有套接字权限

------有,继续检索doSocket类套接字权限

-----doSocket为特权类,他会设置断点,阻止存取控制继续检索,从而返回,

-----存取控制器返回“真”,套接字访问顺利完成


如果A类的对象,被任何其他类型的对象调用,A都能有特权访问套接字了


例子的分析,大家可能有疑惑,我们更进一步分析下

-----程序执行首先要进堆栈,然后出栈,

-----语句AccessController.doPrivileged(ds),会让ds.run靠后进栈,之后让new Socket("localhost",6000)最后进栈,也就是说存取控制器回调run,

-----new Socket("localhost",6000)会进一步让AccessController.checkPermission()方法进栈,checkPermission()才是检索类权限的真正指令流

-----等checkPermission()执行完毕后,知道有权限才让new Socket("localhost",6000)正常返回,否则,如果没有权限,抛出运行时异常,堆栈结束了

-----AccessController.doPrivilleged()是很早进入栈的,但他进栈时,在栈中设置了一个断点,断点会阻止checkPermission()方法中检索代码的执行,

-----因此checkPermission()执行中途,检测到堆栈断点,返回,不会去检索栈中其他类的权限了,从而达到了特权的目的。


没法画图,唉







0 0
原创粉丝点击