在HDFS中,也提供了类似于linux文件系统中的文件操作权限管理功能,当我们在HDFS中创建一个文件/目录的时候,一般会为这个文件/目录附加对应的创建者、操作权限码。这里的操作权限码与linux中文件操作模式是完全一样的,如:0x777。那么,当一个客户端在对一个文件/目录进行操作(创建一个文件,读、写文件等)之前,先要对这个客户端进行操作权限的验证。其实,一个用户在使用HDFS提供的客户端来访问HDFS的时候,这个客户端是具有某个用户登录的,它和HDFS的NameNode节点连接的时候会把这个用户信息发送NameNode,在NameNode节点上,我也可以配置一个超用户和一个超用户组信息,如图所示:
首先在这里要说的就是客户端的用户信息的配置,这个用户信息可以来自客户端的配置文件core-site.xml也可以来自当前操作系统的登录用户,但是优先考虑配置文件core-site.xml中的用户信息,对应的配置项为:hadoop.job.ugi,这个配置项的值可以是一个用户名和一个用户组,或者是一个用户名和多个用户组。如果配置文件没有配置登录用户信息的话,就会自动执行系统的shell命令whoami、bash -c groups来分别获取当前系统的用户名和用户所属的组。在NameNode节点上超用户信息配置同客户端的用户配置是相同的,而超用户组的信息只能通过配置文件hdfs-site来配置了,对应的配置项为:dfs.permissions.supergroup。
刚才说过,当用户需要对某个文件进的时候就需要先验证该用户对该文件是否具有对应的操作权限,那么这个工作就交由权限检查器——PermissionChecker来处理了。
1.为客户端用户创建一个权限检查器
PermissionChecker(String fsOwner, String supergroup) throws AccessControlException{
//获取客户端的用户信息
UserGroupInformation ugi = UserGroupInformation.getCurrentUGI();
if (LOG.isDebugEnabled()) {
LOG.debug("ugi=" + ugi);
}
if (ugi != null) {
user = ugi.getUserName();//客户端的用户名
groups.addAll(Arrays.asList(ugi.getGroupNames()));//客户端的用户所属的组
isSuper = user.equals(fsOwner) || groups.contains(supergroup);//判断用户是不是超用户
}
else {
throw new AccessControlException("ugi = null");
}
}
在多线程的环境下,NameNode是如何准确地获取到当前客户端对应的用户信息的,HDFS采用了JAAS技术来实现,在这里,我并不打算详细描述。
2.检查客户端用户是否有权限操作某文件
void checkPermission(String path, INodeDirectory root, boolean doCheckOwner, FsAction ancestorAccess, FsAction parentAccess, FsAction access, FsAction subAccess) throws AccessControlException {
if (LOG.isDebugEnabled()) {
LOG.debug("ACCESS CHECK: " + this
+ ", doCheckOwner=" + doCheckOwner
+ ", ancestorAccess=" + ancestorAccess
+ ", parentAccess=" + parentAccess
+ ", access=" + access
+ ", subAccess=" + subAccess);
}
synchronized(root) {
INode[] inodes = root.getExistingPathINodes(path);//获取文件的所有父节点
int ancestorIndex = inodes.length - 2;
for(; ancestorIndex >= 0 && inodes[ancestorIndex] == null; ancestorIndex--);
checkTraverse(inodes, ancestorIndex);
if (ancestorAccess != null && inodes.length > 1) {
check(inodes, ancestorIndex, ancestorAccess);
}
if (parentAccess != null && inodes.length > 1) {
check(inodes, inodes.length - 2, parentAccess);
}
if (access != null) {
check(inodes[inodes.length - 1], access);
}
if (subAccess != null) {
checkSubAccess(inodes[inodes.length - 1], subAccess);
}
if (doCheckOwner) {
checkOwner(inodes[inodes.length - 1]);
}
}
}
private void checkOwner(INode inode) throws AccessControlException {
if (inode != null && user.equals(inode.getUserName())) {
return;
}
throw new AccessControlException("Permission denied");
}
private void checkTraverse(INode[] inodes, int last) throws AccessControlException {
for(int j = 0; j <= last; j++) {
check(inodes[j], FsAction.EXECUTE);
}
}
private void checkSubAccess(INode inode, FsAction access
) throws AccessControlException {
if (inode == null || !inode.isDirectory()) {
return;
}
Stack<INodeDirectory> directories = new Stack<INodeDirectory>();
for(directories.push((INodeDirectory)inode); !directories.isEmpty(); ) {
INodeDirectory d = directories.pop();
check(d, access);
for(INode child : d.getChildren()) {
if (child.isDirectory()) {
directories.push((INodeDirectory)child);
}
}
}
}
private void check(INode[] inodes, int i, FsAction access) throws AccessControlException {
check(i >= 0? inodes[i]: null, access);
}
private void check(INode inode, FsAction access) throws AccessControlException {
if (inode == null) {
return;
}
FsPermission mode = inode.getFsPermission();//获取该文件的权限操作码
if (user.equals(inode.getUserName())) { //用户名相同
if (mode.getUserAction().implies(access)) { return; }
}
else if (groups.contains(inode.getGroupName())) { //用户组相同
if (mode.getGroupAction().implies(access)) { return; }
}
else { //other class
if (mode.getOtherAction().implies(access)) { return; }
}
throw new AccessControlException("Permission denied: user=" + user + ", access=" + access + ", inode=" + inode);
}
很多初学者在操作HDFS中的文件时经常会遇到AccessControlException异常,但是这个文件其实是自己之前建立的,按理说不可能出现“无权访问”这样的问题的,而造成这个异常的一个可能的场景是:没有通过配置文件来配置客户端的用户信息,用OS的用户user1创建了文件,之后又用OS的用户user2来删除该文件,此时却发现无权删除该文件,所以出现这一类异常的根本原因就是客户端的登录用户信息配置不正确,导致他无法操作自己想要的文件。如果出现这类操作异常的话,请仔细阅读本文。