• [转]HDFS客户端的权限错误:Permission denied


    搭建了一个Hadoop的环境,Hadoop集群环境部署在几个Linux服务器上,现在想使用windows上的Java客户端来操作集群中的HDFS文件,但是在客户端运行时出现了如下的认证错误。

    错误的详细描述如下:

    org.apache.hadoop.security.AccessControlException: org.apache.hadoop.security .AccessControlException: Permission denied: user=Administrator, access=WRITE, inode="hadoop": hadoop:supergroup:rwxr-xr-x

    其实这个错误的原因很容易看出来,用户Administator在hadoop上执行写操作时被权限系统拒绝.

    解决问题的过程

    看到这个错误的,第一步就是将这个错误直接入放到百度google里面进行搜索。找到了N多篇文章,但是主要的思路就如此篇文章所写的两个解决办法:http://www.cnblogs.com/acmy/archive/2011/10/28/2227901.html

    1、在hdfs的配置文件中,将dfs.permissions修改为False

    2、执行这样的操作 hadoop fs -chmod 777 /user/hadoop

    对于上面的第一个方法,我试了行不通,不知道是自己设置错误还是其他原因,对我此法不可行,第二个方法可行。第二个方法是让我们来修改HDFS中相应文件夹的权限,后面的/user/hadoop这个路径为HDFS中的文件路径,这样修改之后就让我们的administrator有在HDFS的相应目录下有写文件的权限(所有的用户都是写权限)。

    虽然上面的第二步可以解决问题了,上传之后的文件所有者为Administrator,但是总感觉这样的方法不够优雅,而且这样修改权限会有一定的安全问题,总之就是看着不爽,就在想有没有其他的办法?

    问题分析

    开始仔细的观察了这个错误的详细信息,看到user=Administrator, access=WRITE。这里的user其实是我当前系统(运行客户端的计算机的操作系统)的用户名,实际期望这里的user=hadoop(hadoop是我的HADOOP上面的用户名),但是它取的是当前的系统的用户名,很明显,如果我将当前系统的用户名改为hadoop,这个肯定也是可以行得通的,但是如果后期将开发的代码部署到服务器上之后,就不能方便的修改用户,此方法明显也不够方便。

    现在就想着Configuration这个是一个配置类,有没有一个参数是可以在某个地方设置以哪个用户运行呢?搜索了半天,无果。没有找到相关的配置参数。

    最终只有继续分析代码, FileSystem fs = FileSystem.get(URI.create(dest), conf);代码是在此处开始对HDFS进行调用,所以就想着将HADOOP的源码下下来,debug整个调用过程,这个user=Administator是在什么时间赋予的值。理解了调用过程,还怕找不到解决问题的办法么?

    跟踪代码进入 FileSystem.get-->CACHE.get()-->Key key = new Key(uri, conf);到这里的时候发现key值里面已经有Administrator了,所以关键肯定是在new key的过程。继续跟踪UserGroupInformation.getCurrentUser()-->getLoginUser()-->login.login()到这一步的时候发现用户名已经确定了,但是这个方法是Java的核心源码,是一个通用的安全认证,但对这一块不熟悉,但是debug时看到subject里面有NTUserPrincipal:Administator,所以就想着搜索一下这个东西是啥,结果就找到了下面这一篇关键的文章:

    http://www.udpwork.com/item/7047.html

    在此篇文章里面作者分析了hadoop的整个登录过程,对于我有用的是其中的这一段:

    2.login.login();
    这个会调用HadoopLoginModule的login()和commit()方法。
    HadoopLoginModule的login()方法是一个空函数,只打印了一行调试日志 LOG.debug("hadoop login");
    commit()方法负责把Principal添加到Subject中。
    此时一个首要问题是username是什么?
    在使用了kerberos的情况下,从javax.security.auth.kerberos.KerberosPrincipal的实例获取username。
    在未使用kerberos的情况下,优先读取HADOOP_USER_NAME这个系统环境变量,如果不为空,那么拿它作username。否则,读取HADOOP_USER_NAME这个java环境变量。否则,从com.sun.security.auth.NTUserPrincipal或者com.sun.security.auth.UnixPrincipal的实例获取username。
    如果以上尝试都失败,那么抛出异常LoginException("Can’t find user name")。
    最终拿username构造org.apache.hadoop.security.User的实例添加到Subject中。

    看完这一段,我明白了执行login.login的时候调用了hadoop里面的HadoopLoginModule方法,而关键是在commit方法里面,在这里优先读取HADOOP_USER_NAME系统环境变量,然后是java环境变量,如果再没有就从NTUserPrincipal等里面取。关键代码为:

    if (!isSecurityEnabled() && (user == null)) {
      String envUser = System.getenv(HADOOP_USER_NAME);
      if (envUser == null) {
        envUser = System.getProperty(HADOOP_USER_NAME);
      }
      user = envUser == null ? null : new User(envUser);
    }

    OK,看到这里我的需求也就解决了,只要在系统的环境变量里面添加HADOOP_USER_NAME=hadoop(HDFS上的有权限的用户,具体看自己的情况),或者在当前JDK的变量参数里面添加HADOOP_USER_NAME这个Java变量即可。我的情况添加系统环境变量更方法。

    如果是在Eclipse里面运行,修改完环境变量后,记得重启一下eclipse,不然可能不会生效。

    解决办法

    最终,总结下来解决办法大概有三种:

    1. 在系统的环境变量或java JVM变量里面添加HADOOP_USER_NAME,这个值具体等于多少看自己的情况,以后会运行HADOOP上的Linux的用户名。(修改完重启eclipse,不然可能不生效)
    2. 将当前系统的帐号修改为hadoop
    3. 使用HDFS的命令行接口修改相应目录的权限,hadoop fs -chmod 777 /user,后面的/user是要上传文件的路径,不同的情况可能不一样,比如要上传的文件路径为hdfs://namenode/user/xxx.doc,则这样的修改可以,如果要上传的文件路径为hdfs://namenode/java/xxx.doc,则要修改的为hadoop fs -chmod 777 /java或者hadoop fs -chmod 777 /,java的那个需要先在HDFS里面建立Java目录,后面的这个是为根目录调整权限。

    我喜欢第一个方法。

    附录:HDFS Java API调用代码

    package com.hdfs;
    /**
     * @ClassName: HdfsTest
     * @Description: TODO
     * @author: Qiaozhen
     * @Date: 2015-05-11 13:08:39
     */
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URI;
    
    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.fs.FSDataOutputStream;
    import org.apache.hadoop.fs.FileStatus;
    import org.apache.hadoop.fs.FileSystem;
    import org.apache.hadoop.fs.Path;
    import org.apache.hadoop.io.IOUtils;
    
    public class HdfsTest {
        static String hdfsURL = "hdfs://52.1.123.1:49000/";
        
        //创建新文件
        public static void createFile(String dst , byte[] contents) throws IOException{
            Configuration conf = new Configuration();
            FileSystem fs = FileSystem.get(URI.create(hdfsURL),conf);
            Path dstPath = new Path(dst); //目标路径
            //打开一个输出流
            FSDataOutputStream outputStream = fs.create(dstPath);
            outputStream.write(contents);
            outputStream.close();
            fs.close();
            System.out.println("文件创建成功!");
        }
        
        //上传本地文件
        public static void uploadFile(String src,String dst) throws IOException{
            Configuration conf = new Configuration();
            FileSystem fs = FileSystem.get(URI.create(hdfsURL),conf);
            Path srcPath = new Path(src); //原路径
            Path dstPath = new Path(dst); //目标路径
            //调用文件系统的文件复制函数,前面参数是指是否删除原文件,true为删除,默认为false
            fs.copyFromLocalFile(false,srcPath, dstPath);
            
            //打印文件路径
            System.out.println("Upload to "+conf.get("fs.default.name"));
            System.out.println("------------list files------------"+"
    ");
            FileStatus [] fileStatus = fs.listStatus(dstPath);
            for (FileStatus file : fileStatus) 
            {
                System.out.println(file.getPath());
            }
            fs.close();
        }
        
        //文件重命名
        public static void rename(String oldName,String newName) throws IOException{
            Configuration conf = new Configuration();
            FileSystem fs = FileSystem.get(URI.create(hdfsURL),conf);
            Path oldPath = new Path(oldName);
            Path newPath = new Path(newName);
            boolean isok = fs.rename(oldPath, newPath);
            if(isok){
                System.out.println("rename ok!");
            }else{
                System.out.println("rename failure");
            }
            fs.close();
        }
        //删除文件
        public static void delete(String filePath) throws IOException{
            Configuration conf = new Configuration();
            FileSystem fs = FileSystem.get(URI.create(hdfsURL),conf);
            Path path = new Path(filePath);
            boolean isok = fs.deleteOnExit(path);
            if(isok){
                System.out.println("delete ok!");
            }else{
                System.out.println("delete failure");
            }
            fs.close();
        }
        
        //创建目录
        public static void mkdir(String path) throws IOException{
            Configuration conf = new Configuration();
            FileSystem fs = FileSystem.get(URI.create(hdfsURL),conf);
            Path srcPath = new Path(path);
            boolean isok = fs.mkdirs(srcPath);
            if(isok){
                System.out.println("create dir ok!");
            }else{
                System.out.println("create dir failure");
            }
            fs.close();
        }
        
        //读取文件的内容
        public static void readFile(String filePath) throws IOException{
            Configuration conf = new Configuration();
            FileSystem fs = FileSystem.get(URI.create(hdfsURL),conf);
            Path srcPath = new Path(filePath);
            InputStream in = null;
            try {
                in = fs.open(srcPath);
                IOUtils.copyBytes(in, System.out, 4096, false); //复制到标准输出流
            } finally {
                IOUtils.closeStream(in);
            }
        }
        
        
        public static void main(String[] args) throws IOException {
            //测试上传文件
            uploadFile("E:\tt.txt", "/tmp/");
            //测试创建文件
            /*byte[] contents =  "hello world 世界你好
    ".getBytes();
            createFile("/tmp/d.txt",contents);*/
            //测试重命名
            //rename("/tmp/tt.txt", "/tmp/dd.txt");
            //测试删除文件
          //  delete("/2015/02/2015-02-01.dat"); //使用相对路径
            //delete("test1");    //删除目录
            //测试新建目录
            //mkdir("test1");
            //测试读取文件
            readFile("/tmp/tt.txt");
        }
    
    }
    //上传本地文件
        public void appendFile(String srcPath, String dstPath , String fileName) throws IOException{
            Configuration conf = new Configuration();
            conf.setBoolean("dfs.support.broken.append", true);
            FileSystem fs = FileSystem.get(URI.create(hdfsURL),conf);
            
            InputStream in = new 
                  BufferedInputStream(new FileInputStream(srcPath + "//"+fileName));
            if(!checkdir(dstPath + "//"+fileName)){
                AppendToFile.appendMethodA(log, "Creat new file and upload!");
                uploadFile(srcPath + "//"+fileName,dstPath);
            }else{
                AppendToFile.appendMethodA(log, "Appending to "+dstPath + "//"+fileName + ", fileName: " + fileName);
                OutputStream out = fs.append(new Path(dstPath + "//"+fileName));
                IOUtils.copyBytes(in, out, 4096, true);
                AppendToFile.appendMethodA(log, "Append OK!");
            }
            
            
        }
  • 相关阅读:
    HDU 5642 King's Order 动态规划
    HDU 5640 King's Cake GCD
    HDU 5641 King's Phone 模拟
    HDU 5299 Circles Game 博弈论 暴力
    HDU 5294 Tricks Device 网络流 最短路
    HDU 5289 Assignment rmq
    HDU 5288 OO’s Sequence 水题
    星际争霸 虚空之遗 人族5BB 操作流程
    Codeforces Beta Round #3 D. Least Cost Bracket Sequence 优先队列
    Codeforces Beta Round #3 C. Tic-tac-toe 模拟题
  • 原文地址:https://www.cnblogs.com/dorothychai/p/4494981.html
Copyright © 2020-2023  润新知