该类继承自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的工作原理

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

被折叠的 条评论
为什么被折叠?



