Java 日看一类(20)之IO包中的FilePermission类与FilePermissionCollection类

本文详细解析了FilePermission类的实现机制,介绍了如何通过路径名和操作权限字符串管理文件和目录访问权限,同时深入探讨了权限检查的逻辑。

该类继承自Permission类,并完成了Serializable接口

引入了如下包:

import java.security.*;
import java.util.Enumeration;
import java.util.List;
import java.util.ArrayList;
import java.util.Vector;
import java.util.Collections;
import sun.security.util.SecurityConstants;



该类的类头注释如下:

/**
 * This class represents access to a file or directory.  A FilePermission consists
 * of a pathname and a set of actions valid for that pathname.
 * <P>
 * Pathname is the pathname of the file or directory granted the specified
 * actions. A pathname that ends in "/*" (where "/" is
 * the file separator character, <code>File.separatorChar</code>) indicates
 * all the files and directories contained in that directory. A pathname
 * that ends with "/-" indicates (recursively) all files
 * and subdirectories contained in that directory. A pathname consisting of
 * the special token "&lt;&lt;ALL FILES&gt;&gt;" matches <b>any</b> file.
 * <P>
 * Note: A pathname consisting of a single "*" indicates all the files
 * in the current directory, while a pathname consisting of a single "-"
 * indicates all the files in the current directory and
 * (recursively) all files and subdirectories contained in the current
 * directory.
 * <P>
 * The actions to be granted are passed to the constructor in a string containing
 * a list of one or more comma-separated keywords. The possible keywords are
 * "read", "write", "execute", "delete", and "readlink". Their meaning is
 * defined as follows:
 *
 * <DL>
 *    <DT> read <DD> read permission
 *    <DT> write <DD> write permission
 *    <DT> execute
 *    <DD> execute permission. Allows <code>Runtime.exec</code> to
 *         be called. Corresponds to <code>SecurityManager.checkExec</code>.
 *    <DT> delete
 *    <DD> delete permission. Allows <code>File.delete</code> to
 *         be called. Corresponds to <code>SecurityManager.checkDelete</code>.
 *    <DT> readlink
 *    <DD> read link permission. Allows the target of a
 *         <a href="../nio/file/package-summary.html#links">symbolic link</a>
 *         to be read by invoking the {@link java.nio.file.Files#readSymbolicLink
 *         readSymbolicLink } method.
 * </DL>
 * <P>
 * The actions string is converted to lowercase before processing.
 * <P>
 * Be careful when granting FilePermissions. Think about the implications
 * of granting read and especially write access to various files and
 * directories. The "&lt;&lt;ALL FILES&gt;&gt;" permission with write action is
 * especially dangerous. This grants permission to write to the entire
 * file system. One thing this effectively allows is replacement of the
 * system binary, including the JVM runtime environment.
 *
 * <p>Please note: Code can always read a file from the same
 * directory it's in (or a subdirectory of that directory); it does not
 * need explicit permission to do so.
 *
 * @see java.security.Permission
 * @see java.security.Permissions
 * @see java.security.PermissionCollection
 *
 *
 * @author Marianne Mueller
 * @author Roland Schemers
 * @since 1.2
 *
 * @serial exclude
 */

大意如下:

这个类表示对文件和目录的访问

FilePermission包括了一个路径名和一系列对该路径的有效操作

路径名是被授予指定操作的文件的路径名

一个路径名以‘/’(该文件系统的路径分隔符为‘/’)结尾时,表示该路径下的所有文件和目录

一个路径名以‘/-’为路径结尾时,递归的表示该路径下的所有文件和子目录(就是说子目录下的目录和文件也被包含

可以用特殊字段‘<<ALL FILES>>’作为路径名来匹配所有的文件和目录


注:

由单个‘*’组成的路径表示当前路径下的所有文件及目录,由单个‘-’组成的路径递归表示当前路径下的所有文件及目录


将需要授予操作的关键字以字符串形式传递给构造函数

该字符串包含一个或多个关键字,用逗号相隔

可能使用的关键字有:‘read’,‘write’,‘execute’,‘delete’,‘readlink’

关键字对应操作如下:

read:读权限

write:写权限

execute:执行权限,允许调用Runtime.exec,符合SecurityManager.CheckExec的要求

delete:删除权限,允许调用File.delete,符合SecurityManager.CheckDelete的要求

readlink:读取链接权限,允许通过调用Java.nio.file.Files中的readSymbolicLink方法来读取链接对象


操作字符串在操作前被转化为小写


请小心的授权FilePermission,对各种目录和文件进行读和写权限授予时(尤其是写权限)请三思而后行

对<<All Files>>授予写权限是特别危险的

这个将会授予其对整个文件系统的修改权限

事实上他甚至允许了对系统二进制文件进行替换(包括java虚拟机的运行环境


该类总是有对其目录下文件(包含子目录的读取权限)的读权限(不包含写权限),不需要显式授权



该类含有如下的成员变量:

执行操作

private final static int EXECUTE = 0x1;

写入操作

private final static int WRITE   = 0x2;

读取操作

private final static int READ    = 0x4;

删除操作

private final static int DELETE  = 0x8;

读取链接操作

private final static int READLINK    = 0x10;

全部操作

private final static int ALL     = READ|WRITE|EXECUTE|DELETE|READLINK;

无操作

private final static int NONE    = 0x0;

当前操作(就是操作的int值

private transient int mask;

标记路径是否包含目录

private transient boolean directory;

标记是否递归

private transient boolean recursive;

权限字符串

private String actions;

规范化后的路径

private transient String cpath;

特殊字符-(递归

private static final char RECURSIVE_CHAR = '-';

特殊字符*(不递归

private static final char WILD_CHAR = '*';

串行序列号

private static final long serialVersionUID = 7930732926638008763L;




该类含有如下的方法:

初始化该类的功能(非构造函数

private void init(int mask) {
    if ((mask & ALL) != mask)//不是规定操作关键字之一
            throw new IllegalArgumentException("invalid actions mask");

    if (mask == NONE)//未授予任何操作
            throw new IllegalArgumentException("invalid actions mask");

    if ((cpath = getName()) == null)//路径名为空
            throw new NullPointerException("name can't be null");

    this.mask = mask;//当前操作置入

    if (cpath.equals("<<ALL FILES>>")) {//若路径为全文件系统
        directory = true;//判定为目录
        recursive = true;//判定可递归
        cpath = "";//路径为空
        return;
    }

    // store only the canonical cpath if possible
    cpath = AccessController.doPrivileged(new PrivilegedAction<String>() {//权限检查解除,创建String类的特权操作
        public String run() {//实现run函数
            try {
                String path = cpath;
                if (cpath.endsWith("*")) {//如果路径由*结尾
                    // call getCanonicalPath with a path with wildcard character
                    // replaced to avoid calling it with paths that are
                    // intended to match all entries in a directory
                    path = path.substring(0, path.length()-1) + "-";//提取*之前的路径并在之后加上-
                    path = new File(path).getCanonicalPath();//获得上一步提取后的标准化路径
                    return path.substring(0, path.length()-1) + "*";//提取获得标准路径‘-’前的的路径并在最后加上*
                } else {
                    return new File(path).getCanonicalPath();//直接获得标准路径
                }
            } catch (IOException ioe) {
                return cpath;
            }
        }
    });

    int len = cpath.length();
    char last = ((len > 0) ? cpath.charAt(len - 1) : 0);//获得标准路径的最后一个字符(如果路径为空则也为空

    if (last == RECURSIVE_CHAR &&
        cpath.charAt(len - 2) == File.separatorChar) {//最后一个字符为-且倒数第二个字符为系统默认路径分隔符
        directory = true;//属性为目录
        recursive = true;//允许递归
        cpath = cpath.substring(0, --len);//路径中除去-
    } else if (last == WILD_CHAR &&
        cpath.charAt(len - 2) == File.separatorChar) {//最后一个字符为*
        directory = true;//判定为目录
        //recursive = false;
        cpath = cpath.substring(0, --len);
    } else {//都不是就什么都不用管
        // overkill since they are initialized to false, but
        // commented out here to remind us...
        //directory = false;
        //recursive = false;
    }

    // XXX: at this point the path should be absolute. die if it isn't?
}

构造函数(传入未被标准化的抽象路径,传入操作权限字符串

public FilePermission(String path, String actions) {
    super(path);//调用Permission的构造函数
    init(getMask(actions));//将操作权限字符串转化为整形并传入初始化函数
}

构造函数(仅本包中的类可以访问,传入的是转化好的整形操作权限

FilePermission(String path, int mask) {
    super(path);
    init(mask);
}

检查当前FilePermission对象的是否含有指定的隐藏权限(说明了该类实质就是权限标记和与权限标记相关的一系列操作

public boolean implies(Permission p) {
    if (!(p instanceof FilePermission))//检测p是否为FilePermission对象
        return false;

    FilePermission that = (FilePermission) p;

    // we get the effective mask. i.e., the "and" of this and that.
    // They must be equal to that.mask for implies to return true.

    return ((this.mask & that.mask) == that.mask) && impliesIgnoreMask(that);//若当前的FilePermission对象含有的权限拥有传入对象的所有权限甚至还多则为真
}

表示是否可忽略权限(传入路径为当前路径子集则可以忽略对传入路径权限的声明

boolean impliesIgnoreMask(FilePermission that) {
    if (this.directory) {//如果权限对应的是目录
        if (this.recursive) {//如果当前目录可递归
            // make sure that.path is longer then path so
            // something like /foo/- does not imply /foo
            if (that.directory) {//传入的权限对应也为目录
                return (that.cpath.length() >= this.cpath.length()) &&
                        that.cpath.startsWith(this.cpath);//判定传入的FilePermission是否为当前对象的子路径
            }  else {//若为文件,判定是否为当前路径下的文件
                return ((that.cpath.length() > this.cpath.length()) &&
                    that.cpath.startsWith(this.cpath));
            }
        } else {//不可递归
            if (that.directory) {//传入的为目录
                // if the permission passed in is a directory
                // specification, make sure that a non-recursive
                // permission (i.e., this object) can't imply a recursive
                // permission.
                if (that.recursive)//如果传入的对象可递归则返回False
                    return false;
                else
                    return (this.cpath.equals(that.cpath));//不可递归则判定两路径是否相等(因为均不可递归,不能为子目录
            } else {//传入的为文件
                int last = that.cpath.lastIndexOf(File.separatorChar);//获得最后一个路径分隔符的位置
                if (last == -1)//不存在路径分隔符
                    return false;
                else {
                    // this.cpath.equals(that.cpath.substring(0, last+1));
                    // Use regionMatches to avoid creating new string
                    return (this.cpath.length() == (last + 1)) &&//当前目录为该文件所在目录(最后一位是*)
                        this.cpath.regionMatches(0, that.cpath, 0, last+1);
                }
            }
        }
    } else if (that.directory) {//如果当前路径并非目录且传入的为目录
        // if this is NOT recursive/wildcarded,
        // do not let it imply a recursive/wildcarded permission
        return false;
    } else {//均为文件
        return (this.cpath.equals(that.cpath));
    }
}
对象判等(传入对象与当前对象判等
public boolean equals(Object obj) {
    if (obj == this)
        return true;

    if (! (obj instanceof FilePermission))
        return false;

    FilePermission that = (FilePermission) obj;

    return (this.mask == that.mask) &&
        this.cpath.equals(that.cpath) &&
        (this.directory == that.directory) &&
        (this.recursive == that.recursive);
}
返回该类的hash值
public int hashCode() {
    return 0;
}
将传入的字符串对应权限用int值替换
private static int getMask(String actions) {
    int mask = NONE;//权限赋空

    // Null action valid?
    if (actions == null) {//不存在权限操作
        return mask;
    }

    // Use object identity comparison against known-interned strings for
    // performance benefit (these values are used heavily within the JDK).
    if (actions == SecurityConstants.FILE_READ_ACTION) {//引入的sun包,这一段主要是单个权限赋予的判定(这些其实都是声明的字符串变量,理解为define即可
        return READ;
    } else if (actions == SecurityConstants.FILE_WRITE_ACTION) {
        return WRITE;
    } else if (actions == SecurityConstants.FILE_EXECUTE_ACTION) {
        return EXECUTE;
    } else if (actions == SecurityConstants.FILE_DELETE_ACTION) {
        return DELETE;
    } else if (actions == SecurityConstants.FILE_READLINK_ACTION) {
        return READLINK;
    }

    char[] a = actions.toCharArray();//获得字符数组

    int i = a.length - 1;
    if (i < 0)
        return mask;

    while (i != -1) {//多权限检查
        char c;

        // skip whitespace
        while ((i!=-1) && ((c = a[i]) == ' ' ||
                           c == '\r' ||
                           c == '\n' ||
                           c == '\f' ||
                           c == '\t'))//跳过空格、换行、输入、新行符、结束符等,从末尾开始loop,比较少见
            i--;

        // check for the known strings
        int matchlen;//匹配长度

        if (i >= 3 && (a[i-3] == 'r' || a[i-3] == 'R') &&
                      (a[i-2] == 'e' || a[i-2] == 'E') &&
                      (a[i-1] == 'a' || a[i-1] == 'A') &&
                      (a[i] == 'd' || a[i] == 'D'))
        {
            matchlen = 4;
            mask |= READ;//按位或

        } else if (i >= 4 && (a[i-4] == 'w' || a[i-4] == 'W') &&
                             (a[i-3] == 'r' || a[i-3] == 'R') &&
                             (a[i-2] == 'i' || a[i-2] == 'I') &&
                             (a[i-1] == 't' || a[i-1] == 'T') &&
                             (a[i] == 'e' || a[i] == 'E'))
        {
            matchlen = 5;
            mask |= WRITE;

        } else if (i >= 6 && (a[i-6] == 'e' || a[i-6] == 'E') &&
                             (a[i-5] == 'x' || a[i-5] == 'X') &&
                             (a[i-4] == 'e' || a[i-4] == 'E') &&
                             (a[i-3] == 'c' || a[i-3] == 'C') &&
                             (a[i-2] == 'u' || a[i-2] == 'U') &&
                             (a[i-1] == 't' || a[i-1] == 'T') &&
                             (a[i] == 'e' || a[i] == 'E'))
        {
            matchlen = 7;
            mask |= EXECUTE;

        } else if (i >= 5 && (a[i-5] == 'd' || a[i-5] == 'D') &&
                             (a[i-4] == 'e' || a[i-4] == 'E') &&
                             (a[i-3] == 'l' || a[i-3] == 'L') &&
                             (a[i-2] == 'e' || a[i-2] == 'E') &&
                             (a[i-1] == 't' || a[i-1] == 'T') &&
                             (a[i] == 'e' || a[i] == 'E'))
        {
            matchlen = 6;
            mask |= DELETE;

        } else if (i >= 7 && (a[i-7] == 'r' || a[i-7] == 'R') &&
                             (a[i-6] == 'e' || a[i-6] == 'E') &&
                             (a[i-5] == 'a' || a[i-5] == 'A') &&
                             (a[i-4] == 'd' || a[i-4] == 'D') &&
                             (a[i-3] == 'l' || a[i-3] == 'L') &&
                             (a[i-2] == 'i' || a[i-2] == 'I') &&
                             (a[i-1] == 'n' || a[i-1] == 'N') &&
                             (a[i] == 'k' || a[i] == 'K'))
        {
            matchlen = 8;
            mask |= READLINK;

        } else {//错误权限
            // parse error
            throw new IllegalArgumentException(
                    "invalid permission: " + actions);
        }

        // make sure we didn't just match the tail of a word
        // like "ackbarfaccept".  Also, skip to the comma.
        boolean seencomma = false;//前一个字符是否为‘,’
        while (i >= matchlen && !seencomma) {
            switch(a[i-matchlen]) {//确认刚匹配完的字符前一个为‘,’
            case ',':
                seencomma = true;
                break;
            case ' ': case '\r': case '\n':
            case '\f': case '\t':
                break;//直接回到第一步进行跳过
            default:
                throw new IllegalArgumentException(
                        "invalid permission: " + actions);
            }
            i--;
        }

        // point i at the location of the comma minus one (or -1).
        i -= matchlen;
    }

    return mask;//返回权限
}
获得mask值(权限值)
int getMask() {
    return mask;
}
获得权限名称(mask转回string)
private static String getActions(int mask) {
    StringBuilder sb = new StringBuilder();
    boolean comma = false;//在输入时是否需要逗号分隔,即在判定这个权限前判定的权限其是否拥有

    if ((mask & READ) == READ) {//按位与,判定是否输出read
        comma = true;
        sb.append("read");
    }

    if ((mask & WRITE) == WRITE) {
        if (comma) sb.append(',');//先加入‘,’分隔
        else comma = true;
        sb.append("write");
    }

    if ((mask & EXECUTE) == EXECUTE) {
        if (comma) sb.append(',');
        else comma = true;
        sb.append("execute");
    }

    if ((mask & DELETE) == DELETE) {
        if (comma) sb.append(',');
        else comma = true;
        sb.append("delete");
    }

    if ((mask & READLINK) == READLINK) {
        if (comma) sb.append(',');
        else comma = true;
        sb.append("readlink");
    }

    return sb.toString();
}

获得权限

public String getActions() {
    if (actions == null)
        actions = getActions(this.mask);

    return actions;
}

返回用于储存FilePermission的FilePermissionCollection对象

public PermissionCollection newPermissionCollection() {
    return new FilePermissionCollection();
}

把当前类的状态输出到流中储存(序列化

private void writeObject(ObjectOutputStream s)
    throws IOException
{
    // Write out the actions. The superclass takes care of the name
    // call getActions to make sure actions field is initialized
    if (actions == null)//其实只输出action,就两个非静态变量,一个还是不进序列化的(笑),所以action不能为空
        getActions();
    s.defaultWriteObject();
}

从流中读取类状态(但是权限对应的路径没有了,不是很懂这样序列化还有啥用,可能只能通过对象名来判断所属路径了(笑)

private void readObject(ObjectInputStream s)
     throws IOException, ClassNotFoundException
{
    // Read in the actions, then restore everything else by calling init.
    s.defaultReadObject();
    init(getMask(actions));
}




该类用于给文件授予权限,使用时务必小心




该类文件中还少见的写了第二个类,FilePermissionCollection,由于这个类无法外部访问,所以就放到这里一起讲了

该类继承自PermissionCollection,实现了序列化接口

引入的包同上




类头注释如下

/**
 * A FilePermissionCollection stores a set of FilePermission permissions.
 * FilePermission objects
 * must be stored in a manner that allows them to be inserted in any
 * order, but enable the implies function to evaluate the implies
 * method.
 * For example, if you have two FilePermissions:
 * <OL>
 * <LI> "/tmp/-", "read"
 * <LI> "/tmp/scratch/foo", "write"
 * </OL>
 * And you are calling the implies function with the FilePermission:
 * "/tmp/scratch/foo", "read,write", then the implies function must
 * take into account both the /tmp/- and /tmp/scratch/foo
 * permissions, so the effective permission is "read,write".
 *
 * @see java.security.Permission
 * @see java.security.Permissions
 * @see java.security.PermissionCollection
 *
 *
 * @author Marianne Mueller
 * @author Roland Schemers
 *
 * @serial include
 *
 */

大意如下:

FilePermissionCollection储存了一系列FilePermission的权限

FilePermission对象必须用同一个方法储存,这样才能允许被插入一个队列

但这也使得隐藏权限的检查较为麻烦

例如,有两个FilePermission:

 "/tmp/-", "read"
"/tmp/scratch/foo", "write"
你调用权限检查函数,写入一个FilePermission
"/tmp/scratch/foo", "read,write",

接下来权限检查函数必须考虑到这两条路径的权限,所以最终有效的权限是read、write



该类含有如下成员变量:

储存FilePermission的链表

private transient List<Permission> perms;

序列化ID:

private static final long serialVersionUID = 2202956749081564585L;

文件权限对象的序列化链表

private static final ObjectStreamField[] serialPersistentFields = {
    new ObjectStreamField("permissions", Vector.class),
};


该类含有如下方法:

构造函数(初始化储存空间

public FilePermissionCollection() {
    perms = new ArrayList<>();
}

将要储存的FilePermission加入储存链表

public void add(Permission permission) {
    if (! (permission instanceof FilePermission))//判定对象种类
        throw new IllegalArgumentException("invalid permission: "+
                                           permission);
    if (isReadOnly())//如果是只读属性
        throw new SecurityException(//安全异常
            "attempt to add a Permission to a readonly PermissionCollection");

    synchronized (this) {//线程锁写入
        perms.add(permission);
    }
}

检测是否含有隐藏的指定权限

public boolean implies(Permission permission) {//传入的是需要检测的属性
    if (! (permission instanceof FilePermission))//检测所属类
        return false;

    FilePermission fp = (FilePermission) permission;

    int desired = fp.getMask();//获得需检测权限标志
    int effective = 0;//当前有效权限
    int needed = desired;//需要匹配的权限

    synchronized (this) {
        int len = perms.size();
        for (int i = 0; i < len; i++) {
            FilePermission x = (FilePermission) perms.get(i);
            if (((needed & x.getMask()) != 0) && x.impliesIgnoreMask(fp)) {//传入权限与当前需要检测权限有交集,且传入路径为当前路径的子路径
                effective |=  x.getMask();//获得当前权限
                if ((effective & desired) == desired)//当前权限与传入权限相同则表示含有指定权限
                    return true;
                needed = (desired ^ effective);//异或位运算(表示当前需要的进行匹配的权限,如果不了解这里为什么要用异或的话可以自己把16进制化成二进制异或看看
            }
        }
    }
    return false;
}
返回容器中所有Permission对象的枚举
public Enumeration<Permission> elements() {
    // Convert Iterator into Enumeration
    synchronized (this) {
        return Collections.enumeration(perms);
    }
}

序列化

private void writeObject(ObjectOutputStream out) throws IOException {
    // Don't call out.defaultWriteObject()

    // Write out Vector
    Vector<Permission> permissions = new Vector<>(perms.size());//创建缓冲
    synchronized (this) {
        permissions.addAll(perms);//拷贝写入
    }

    ObjectOutputStream.PutField pfields = out.putFields();
    pfields.put("permissions", permissions);//将当前拷贝出的缓冲写入链表
    out.writeFields();//序列化写出
}

读入 反序列化

private void readObject(ObjectInputStream in)
    throws IOException, ClassNotFoundException
{
    // Don't call defaultReadObject()

    // Read in serialized fields
    ObjectInputStream.GetField gfields = in.readFields();//缓冲区

    // Get the one we want
    @SuppressWarnings("unchecked")
    Vector<Permission> permissions = (Vector<Permission>)gfields.get("permissions", null);//读取链表
    perms = new ArrayList<>(permissions.size());
    perms.addAll(permissions);//拷贝出链表内容
}



该类仅被FilePermission使用,读懂该类有利于理解FilePermission的工作原理


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值