hdfs客户端实例(kerberos+simple)

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sfiayui123/article/details/80919205

1.非安全模式

在非安全模式下,访问hdfs文件系统的客户端代码如下:

package ntci.hadoop.hdfs.test;

import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.security.UserGroupInformation;

import java.io.*;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;


/**
 * Created by ctt on 2017/4/9.
 */
public class HDFSClient {
    public static void main(String[] args) throws IOException {
        try {
            Configuration conf = new Configuration();
  conf.set("fs.defaultFS","hdfs://hadoop-m-21:8020");
   conf.set("fs.hdfs.impl",org.apache.hadoop.hdfs.DistributedFileSystem.class.getName());//如果在pom.xml中配置了如下依赖,可省
 // <groupId>org.apache.hadoop</groupId>
 // <artifactId>hadoop-hdfs</artifactId>
 // <version>2.6.0</version>
  UserGroupInformation.setConfiguration(conf);//初始化用户组信息,并设置用户权限信息。即对UGI设置静态配置,特别是设置安全认证机制和组查找服务。
  FileSystem fs = FileSystem.get(conf);
   FileStatus[] fsStatus = fs.listStatus(new Path("/"));
 for (int i = 0;i < fsStatus.length;i++){
                System.out.println(fsStatus[i].getPath().toString());
   }
           } catch (IOException e) {
             e.printStackTrace();
  } 
    }
}

2.安全模式下(非代理)

2.1为集群配置kerberos

使用域名:HADOOP.COM。

2.2创建测试用户test

在root用户下,使用kadmin.local命令,本地操作KDC数据库:kadmin.local -q “addprinc test”,回车,输入两次test用户的密码:test。
这样,获取到test用户的 principal:test@HADOOP.COM,默认的主机名为:ctt-m-48。

2.3获取test用户的Ticket-Granting ticket

(1)输入kinit test回车,输入其密码test,回车,得到test用户的TGT,或者(2)kinit -k -t test.keytab test@HADOOP.COM(kinit obtains and caches an initial ticket-granting ticket for principal),票据存放在TGT cache:/tmp/krb5cc_*本地缓存中(其中*表示的是用户的ID)。

kinit [-V] [-l lifetime] [-s start_time] [-r renewable_life] [-p | -P] [-f | -F] [-a] [-A] [-C] [-E] [-v] [-R] [-k [-t keytab_file]] [-c cache_name] [-n] [-S service_name] [-I input_ccache] [-T armor_ccache] [-X attribute[=value]] [principal]

可以通过使用命令:klist -e查看test用户的TGT,如下所示,可以看到TGT的创建日期,有效期,属主以及类型等信息。
这里写图片描述

2.4生成test用户的keytab文件test.keytab

在root用户下使用kadmin.local -q “xst -norandkey -k test.keytab test@HADOOP.COM”命令。同时将生成的test.keytab文件放到/etc/security/keytabs文件夹下。

2.5综合以上过程得到test用户的principal和test.keytab文件,用来做请求认证

(1)Ticket-Granting-Ticket(TGT):这张ticket需要用户手动获取,这张ticket是用户获取Service Ticket的凭证。用户需要提供Kerberos principal和密码或者keytab通过Kerberos认证才能够获取TGT,获取后TGT会缓存在用户的客户端(/tmp/krb5cc_*)。

(2)Service Ticket:访问集群中各个服务的凭证。Kerberos会自动根据客户端缓存的TGT来向用户发放Service Ticket,用户手动获取了TGT后 无需 自己获取Service Ticket。

(3)要通过Kerberos的认证需要提供principal及其对应的密码。密码可以手动输入,也可以存放在一个keytab文件中。“Keytab”是“key table”的简写,它用于存放一个或多个principal的密码。进行Kerberos认证时,一个用户可以提供principal和密码,或者principal和keytab文件。如果使用principal和keytab文件认证,那么Kerberos会去keytab文件中读取principal密码。

2.6代码样例

2.6.1功能介绍

HDFS初始化是指在使用HDFS提供的API之前,需要做的必要工作。过程为:加载HDFS服务配置文件,并进行kerberos安全认证,认证通过后再实例化Filesystem,之后使用HDFS的API。

2.6.2配置文件介绍
文件 作用
Core-site.xml 配置hdfs详细参数。
Hdfs-site.xml 配置hdfs详细参数。
*.keytab 对于Kerberos安全认证提供HDFS用户信息。
krb5.conf Kerberos server配置信息。
2.6.3代码样例
public class HDFSClient {

    public static void main(String[] args) {
        try {
      //1. 初始化Configuration对象,并加载配置文件。
                           Configuration conf = new Configuration();
                           //conf.set("fs.defaultFS","hdfs://ctt-m-48:8020");
                           //conf.set("hadoop.security.authentication","kerberos");
                           conf.addResource(new Path("/usr/hdp/2.5.0.0-1245/hadoop/conf/core-site.xml"));
                           conf.addResource(new Path("/usr/hdp/2.5.0.0-1245/hadoop/conf/hdfs-site.xml"));

       //2. 根据配置文件进行Kerberos安全认证。
                           UserGroupInformation.setConfiguration(conf);//初始化用户组信息,得到kerberos的realm,以及auth_to_local(即principal到本地用户的映射,例如:RULE:[1:1@0](hdfs-hadoop_ctt@HADOOP.COM)s/.*/hdfs/),同时即对UGI设置静态配置,特别是设置安全认证机制和组查找服务。

                           UserGroupInformation.loginUserFromKeytab("test@HADOOP.COM","/etc/security/keytabs/test.keytab");//在此,我们使用用户名(principal)和keytab文件登录,如果选择使用用户名和密码登录,则相应的代码可写为:LoginContext lc = kinit();

      //UserGroupInformation.loginUserFromSubject(lc.getSubject());

      //3. 初始化HDFS Filesystem文件系统操作对象。使用FileSystem对象对HDFS文件系统进行查询,创建、删除目录、文件操作。
                           FileSystem fs = FileSystem.get(conf);
                           fs.copyFromLocalFile(new Path(path), new Path("/tmp/test/test1"));
                           FileStatus[] fsStatus = fs.listStatus(new Path("/"));
                           for (int i = 0;i < fsStatus.length;i++){
                                        System.out.println(fsStatus[i].getPath().toString());
                               }
                                        } catch (IOException e) {
                                                        e.printStackTrace();
                                         }
                               }

           }
2.6.4JAAS架构及使用方法
2.6.4.1 hadoop的身份认证和授权都是建立在JAAS之上

这里写图片描述
Application:JAAS使用者使用Java语言编写的应用。

LoginContext:可以看做JAAS为上层Java应用提供的住入口,上层应用通过调用LoginContext提供的方法,与底层的认证支撑体系对接。

xxxLoginModule:JAAS将底层不同的实际账号认证服务进行抽象,比如RDBMS抽象成RdbmsLoginModule等等。有了LogingModule的抽象能力,让JAAS的使用者可以根据自己的需求选择实际的账号存储体系及账号认证的服务架构,只需要以相对应的LoginModule与上层(LoginContext)相对接上就可以了。

2.6.4.2JAAS的主要使用方法
整个调用就两步:

(1)创建LoginContext实例lc。
(2)调用lc的login方法实现认证。(认证失败时会抛出LoginException)

认证的实际流程:

LoginModule的主要方法:
(1) initialize () :创建LoginModule实例时会被构造函数调用。
(2)login ():进行验证,调用LoginContext的login方法之后,逐个调用所关联的每个LoginModule的login方法。
(3)commit ():当LgoninContext对象接受所有LoginModule对象传回的结果后将调用该方法。该方法将Principal对象和凭证赋给Subject对象。
(4)abort () 当任何一个LoginModule对象验证失败时都会调用该方法。此时没有任何Principal对象或凭证关联到Subject对象上。
(5)logout () 删除与Subject对象关联的Principal对象和凭证。

对于一个LoginModule来说,在用户调用LoginContext的login方法之后,实际经历的主要方法调用流程就是:

(6)成功时调用序列:initialize->login->commit
(7)失败时调用序列:initialize->login->abort

认证过程中的信息存储

在认证的过程当中,当用户进行认证登录之后,其信息需要存储于某个对象当中,以备之后的操作基于该对象提供的方法进行,对象自身携带的信息可以充分说明对象是经过某用户成功认证之后生成的。

Java中,该对象为Subject类的实例,Subject中所包含信息:
(1)Principal(可以简单类比为好理解的username,不严谨)
(2)Credentials(public/private) (可简单类比为password,不严谨)

Subject的中关于对象属性的这些信息,在LoginModule的commit方法中会被赋上正确的值。

2.6.5HADOOP认证的主要实现类

Hadoop认证的实现类为org.apache.hadoop.security.UserGroupInformation。

从上面对JAAS的简述很容易想象,Hadoop为了定义自己的认证机制,实际就是实现了一个自己的LoginModule,在Java应用调用LoginContext的login方法之后,触发Hadoop自定义的LoginModule中的逻辑。

2.6.5.1HadoopConfiguration

UserGroupInformation的内部类HadoopConfiguration
这里写图片描述
通过在HadoopConfiguration()中定义要使用的LoginModule,在Java应用调用LoginContext的login方法之后,触发Hadoop自定义的LoginModule中的逻辑。如上所述,通过kerberos登录的用户使用的AppConfigurationEntry为KEYTAB_KERBEROS_LOGIN, HADOOP_LOGIN两个,其中KEYTAB_KERBEROS_LOGIN调用的登录模块是Krb5LoginModule,而HADOOP_LOGIN调用的登录模块是HadoopLoginModule。接下来,就来看看Krb5LoginModule这一登录模块所做的事情。

2.6.5.2Krb5LoginModule

按照如上的代码所示,在加载过相应的loginEntry也即LoginModule之后,按照成功时调用序列:initialize->login->commit。
(1)初始化该登录模块(Krb5LoginModule):
(2)生成登陆上下文对象定义为login,通过login.login(),登陆。
(3)调用commit()方法负责把Principal(test@HADOOP.COM)添加到Subject中。
这里写图片描述
注:其中ticket cache中存放的主要内容是:current ticket lifetime、session key、krbtgt/service principal。而所有的票据缓存在/tmp/krb5cc_*文件中。

(4)kerberos中相应的credential的组成
这里写图片描述
(5)生成ticket的函数:

 public static KerberosTicket credsToTicket(Credentials var0) {
               EncryptionKey var1 = var0.getSessionKey();
 return new KerberosTicket(var0.getEncoded(), new KerberosPrincipal(var0.getClient().getName()), new KerberosPrincipal(var0.getServer().getName(), 2), var1.getBytes(), var1.getEType(), var0.getFlags(), var0.getAuthTime(), var0.getStartTime(), var0.getEndTime(), var0.getRenewTill(), var0.getClientAddresses());
            }

3.安全模式(代理 :Impersonation)

public class HDFSClient {

          public static void main(String[] args) {
                    try {
                            final Configuration conf = new Configuration();
                            UserGroupInformation.setConfiguration(conf);
                            UserGroupInformation.loginUserFromKeytab("hdfs-hadoop_ctt@HADOOP.COM", "/etc/security/keytabs/hdfs.headless.keytab");
                            UserGroupInformation proxyUser = UserGroupInformation.getCurrentUser();
                            UserGroupInformation ugi = UserGroupInformation.createProxyUser("test", proxyUser);
                            System.out.println(proxyUser);
                            ugi.doAs(new PrivilegedAction<Void>() {
                               @Override
                                  public Void run() {
                                           try {
                                                   FileSystem fs = FileSystem.get(conf);
                                                   FileStatus[] listStatus = fs.listStatus(new Path("/"));
                                                   for (FileStatus status : listStatus) {
                                                   String name = status.getPath().getName();
                                                   //判断是目录还是文件,然后打印name+和判断结果
                                                   System.out.println(name + (status.isDirectory() ? " is dir" : " is file"));
                                                     }
                                                   fs.close();
                                                  } catch (IOException e) {
                                                       e.printStackTrace();
                                             }
                                                   return null;
                                        }
                                      });
                                   } catch (IOException e) {
                                       e.printStackTrace();
                               }
                          }
                    }
展开阅读全文

没有更多推荐了,返回首页