您的位置:首页 > 编程语言 > Java开发

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

2018-03-11 14:09 405 查看
该类继承自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 "<<ALL FILES>>" 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 "<<ALL FILES>>" 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的工作原理
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: