(转)HDFS客户端的权限错误:Permission denied

news/2024/7/7 20:18:44

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

搭建了一个Hadoop的环境,Hadoop集群环境部署在几个Linux服务器上,现在想使用windows上的Java客户端来操作集群中的HDFS文件,但是在客户端运行时出现了如下的认证错误,被折磨了几天,问题终得以解决。以此文记录问题的解决过程。

(如果想看最终解决问题的方法拉到最后,如果想看我的问题解决思路请从上向下看)

问题描述

上传文件的代码:

  1.     private static void uploadToHdfs() throws FileNotFoundException,IOException {
  2.         //我的文件地址
  3.         String localSrc = "E:\\快盘\\技术文档\\hadoop\\HDFS初步研究.pdf";
  4.         //存放在云端的目的地址
  5.         String dest = "hdfs://192.168.2.156:9000/user/HDFS初步研究.pdf";
  6.         InputStream in = new BufferedInputStream(new FileInputStream(localSrc));
  7.         //得到配置对象
  8.         Configuration conf = new Configuration();
  9. //        conf.set("fs.default.name","hdfs://192.168.2.156:9000");
  10.         //文件系统
  11.         FileSystem fs = FileSystem.get(URI.create(dest), conf);
  12.         //输出流
  13.         OutputStream out = fs.create(new Path(dest), new Progressable() {
  14.             @Override
  15.             public void progress() {
  16.                 System.out.println("上传完一个设定缓存区大小容量的文件!");
  17.             }
  18.         });
  19.         //连接两个流,形成通道,使输入流向输出流传输数据
  20.         IOUtils.copyBytes(in, out, 4096, true);
  21.     }

错误的详细描述如下:

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等里面取。关键代码为:

  1. if (!isSecurityEnabled() && (user == null)) {
  2.   String envUser = System.getenv(HADOOP_USER_NAME);
  3.   if (envUser == null) {
  4.     envUser = System.getProperty(HADOOP_USER_NAME);
  5.   }
  6.   user = envUser == null ? null : new User(envUser);
  7. }

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目录,后面的这个是为根目录调整权限。

我喜欢第一个方法。

转载于:https://my.oschina.net/wangbaofeng/blog/706234


http://www.niftyadmin.cn/n/2001633.html

相关文章

linux 用户进程结束后 malloc申请的内存会自动释放吗,当程序退出后,动态申请的内存会自动释放吗...

我们知道程序在运行的过程中是需要占用一定内存的,一般程序所需要的内存由操作系统来分配,由操作系统分配的,自然需要由操作系统回收。但是在实际开发中,用户可以通过一些函数人为地申请内存,再由用户来释放&#xff0…

如何做好IT运营.

定义IT管理的重点在于业务策略与 IT 部门提供的服务之间的一致性。IT 管理可建立必要的管理机制来确保可预测的 IT 服务交付,从而确保业务流程和 IT 流程之间的联系。IT 管理传统上属于CIO、CEO和一些 IT 和业务线(line of business,LOB&…

linux 对比2个目录,linux – 比较2个目录并复制第3个目录中的差异

使用–compare-dest.从手册页:–compare-destDIR –This option instructs rsync to use DIR on the destination machine as an additional hierarchy to compare destination files against doing transfers (if the files are missing in the destination direct…

Java HashMap工作原理及实现

原文出处: Yikun 1. 概述 从本文你可以学习到: 什么时候会使用HashMap?他有什么特点?你知道HashMap的工作原理吗?你知道get和put的原理吗?equals()和hashCode()的都有什么作用?你知道hash的实现…

linux c程序设计大全 下载,The C programming Language[C程序设计语言]PDF

内容简介 Presents a complete guide to ANSI standard C language programming. Written by the developers of C, this new version helps readers keep up with the finalized ANSI standard for C while showing how to take advantage of Cs rich set of operators, …

Metro Win8风格的按钮(Filp翻转)

原地址->http://www.cnblogs.com/yk250/p/5661093.html 介绍:简约而不简单....颜色可随意调制,最好用Blend工具。 效果图如下:话说这个图会不会太大了点 0_0 外观简单,制作也十分简单&#x…

linux学习密码管理,LINUX学习 usermod命令 , mkpasswd命令,用户密码管理

一、usermodusermod是更改用户属性的命令,与userid类似格式:usermod -u 123 usernameusermod -g 123 username二、mkpasswd系统默认下是没有这个工具的,我们需要手动yum安装一下mkpasswd用来生成随机密码字符串。有时我们需要生成指定的长度密…

利用 LINQ的skip和Take 方法对List实现分页效果

1 var testListnew List<string>(); 2 var resulttestList.Skip(pageSize * (pageNum - 1)).Take(pageSize); 3 //skip是跳过的条数&#xff0c;pageSize*(pageIndex-1),Take 是返回多少条数据&#xff0c;也就是pageSize! 转载于:https://www.cnblogs.com/dansediao/p/5…