Windows Kerberos客户端配置并访问CDH

https://forum.huawei.com/enterprise/zh/thread-337775.html

最近被安排做java本地访问带认证的CDH集群,网上查了各种博客,试了各种方法都无法登录上。最终发现是端口问题,因为kdc默认端口是88,所以必须给88端口为UDP协议

 

使用API访问开启安全Kerberos的Hdfs
hadoop集群(cdh集群)在开启kerberos安全认证方式后,通常如果在集群shell客户端通过hadoop dfs命令访问的,经过kinit登录kerberos认证即可 ,如下所示

      如果不进行kinit登录kerberos用户,则不能进行hdfs操作,如图直接会报安全异常! 

è¿éåå¾çæè¿°

 

而如果进行kinit登录后就能进行hdfs操作了,通过kinit user@YOU-REALM 然后输出密码就能在当前交互下获取kerberos票据授权票据 

è¿éåå¾çæè¿°

而如果通过程序在远程进行访问,显然不能再通过kinit来进行登录了,此时需要通过keytab文件来进行访问,keytab文件生成这里不在进行说明,主要说明获取keytab文件后如果通过代码来进行访问

package com.test.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.LocatedFileStatus;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.RemoteIterator;
import org.apache.hadoop.security.UserGroupInformation;

import javax.security.auth.Subject;
import java.io.IOException;
import java.net.URI;
import java.security.PrivilegedExceptionAction;

public class HdfsTest {
    public static void main(String[] args) throws Exception {
        test1();
    }
    //kerberos
    public static void test1() throws Exception{
        //设置java安全krb5配置,其中krb5.conf文件可以从成功开启kerberos的集群任意一台节点/etc/krb5.conf拿到,
        //这里应该也可以直接设置一下两个属性获取 ,我没有测试这个 
        //System.setProperty("java.security.krb5.realm","YOU-REALM.COM");
        //System.setProperty("java.security.krb5.KDC","kdc_hostname");
        System.setProperty("java.security.krb5.conf", "E:\\test\\krb5.conf")
        Configuration conf = new Configuration();
        //这里设置namenode新
        conf.set("fs.defaultFS", "hdfs://namenode:8020");
        //需要增加hadoop开启了安全的配置
        conf.setBoolean("hadoop.security.authorization", true);
        //配置安全认证方式为kerberos
        conf.set("hadoop.security.authentication", "Kerberos");
        //设置namenode的principal
        conf.set("dfs.namenode.kerberos.principal", "hdfs/[email protected]");
        //设置datanode的principal值为“hdfs/[email protected]”
        conf.set("dfs.datanode.kerberos.principal", "hdfs/[email protected]");
        //通过hadoop security下中的 UserGroupInformation类来实现使用keytab文件登录 
        UserGroupInformation.setConfiguration(conf);
        //设置登录的kerberos principal和对应的keytab文件,其中keytab文件需要kdc管理员生成给到开发人员
        UserGroupInformation.loginUserFromKeytab("[email protected]","E:\\test\\user01.keytab");
        //获取带有kerberos验证的文件系统类
        FileSystem fileSystem1 = FileSystem.get(conf);
        //测试访问情况
        Path path=new Path("hdfs://namenodehost:8020/user/user01");
        if(fileSystem1.exists(path)){
            System.out.println("===contains===");
        }
        RemoteIterator list=fileSystem1.listFiles(path,true);
        while (list.hasNext()){
            LocatedFileStatus fileStatus=list.next();
            System.out.println(fileStatus.getPath());
        }
    } 
 }

===============================================================================

用Java访问带有Kerberos认证的HBase

 

 

开始之前

   因为HBase的存储系统是基于Hadoop的存储,现在Hadoop已经增加了Kerberos认证机制,这样HBase的客户端访问HBase数据库的时候也需要进行身份的认证。
   Kerberos是一个认证中心,客户端在访问HBase前必须通过认证才能访问,下图是Kerberos的认证图:
  

 我们不需要详细介绍Kerberos的原理,但是大概流程可以说一下:

当HBase客户端访问HBase的时候,首先必须访问KDC获取一个经过授权的票据,以后Client在访问HBase server的时候可以通过这个票据进行访问。
正常情况下当我们通过HBase客户端访问的时候,都需要进行一次认证的过程,认证过后,KDC返回的票据具有有效期,一般默认是10小时,换句话说在这10个小时内你不需要再次登录KDC进行认证。

1,Linux客户端认证方式
 
 如果我们现在处在Linux客户端上想进行HBase Client的Kerberos认证怎么办,我们怎么登录KDC进行认证?  在Linux有一个Kinit可以完成这种认证过程,可以通过kinit之后,传入必要的参数(例如用户名和密码)等就可以认证。 同时Kerberos还提供了另外一种方式就是我们可以通过一个.keytab  文件直接认证,这样我们就不需要记住这些密码了,keytab这个文件从哪来的呢,这也是在配置Hadoop的时候通过一个ktutil工具生成的。我们可以理解成.keytab文件是带有密码的一个文件就可以了,只要拿到这个文件我们就可以在linux机器上运行kinit 完成这个登录认证的过程 (其中hbase.keytab是服务端生成的文件)

kinit -k -t /etc/hadoop/conf/hbase.keytab hbase/[email protected]

认证完成后我们就可以直接用 hbase shell操作Hbase了

2, Java程序认证方式

通过Linux kinit 命令我们很容易的完成认证,但是如果要是用Java程序编码怎么完成这种登录认证呢?

  2.1 我们把服务器端的 hbase.keytab 文件 copy到 本地一个磁盘,例如 c:\
  2.2 我们copy远程集群一个hbase-site.xml文件放到你运行环境的classpath下,这个和正常没有kerberos的HBase访问是一样的
  2.3 接下来我们需要用到 UserGroupInformation API来进行访问,这个API需要一个参数 principal 和一个你的 keytab 文件,这里文件里面存储的是相关的密码

代码:

package com.hbasedemo;

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.security.UserGroupInformation;

public class Test {
       private static Configuration conf = null;

       static {

             // 这个配置文件主要是记录 kerberos的相关配置信息,例如KDC是哪个IP?默认的realm是哪个?
             // 如果没有这个配置文件这边认证的时候肯定不知道KDC的路径喽
             // 这个文件也是从远程服务器上copy下来的
            System. setProperty("java.security.krb5.conf", "C:/Users/dongzeguang/Downloads/krb5.conf" );
            
             conf = HBaseConfiguration.create();
             conf.set("hadoop.security.authentication" , "Kerberos" );
             // 这个hbase.keytab也是从远程服务器上copy下来的, 里面存储的是密码相关信息
             // 这样我们就不需要交互式输入密码了
             conf.set("keytab.file" , "C:/Users/Downloads/hbase.keytab" );
             // 这个可以理解成用户名信息,也就是Principal
             conf.set("kerberos.principal" , "hbase/[email protected]" );           
            UserGroupInformation. setConfiguration(conf);
             try {
                  UserGroupInformation. loginUserFromKeytab("hbase/[email protected]", "C:/Users/Downloads/hbase.keytab" );
            } catch (IOException e) {
                   // TODO Auto-generated catch block
                  e.printStackTrace();
            }
      }

       public static void scanSpan(final String tableName) throws Exception {
            HTable table =  new HTable(conf, tableName);
            System. out.println("tablename:" +new String(table.getTableName()));
            Scan s = new Scan();
            ResultScanner rs = table.getScanner(s);
            
             for (Result r : rs) {
                  System. out.println(r.toString());
                  KeyValue[] kv = r. raw();
                   for (int i = 0; i < kv.length; i++) {
                        System. out.print(new String(kv[i].getRow()) + "");
                        System. out.print(new String(kv[i].getFamily()) + ":");
                        System. out.print(new String(kv[i].getQualifier() ) + "" );
                        System. out.print(kv[i].getTimestamp() + "" );
                        System. out.println(new String(kv[i].getValue() ));
                  }
            }

      }

       /**
       * @param args
       */
       public static void main(String[] args) {
             // TODO Auto-generated method stub
             try {
                  Test. scanSpan("h_span");
            } catch (Exception e) {
                   // TODO Auto-generated catch block
                  e.printStackTrace();
            }
      }

}
--------------------- 


 

Java访问带有Kerberos认证的Hive

package com.asiainfo.c3.http.service.mvc;

import com.asiainfo.c3.util.Config;
import com.asiainfo.c3.util.HDFSUtil;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.UserGroupInformation;
import java.io.IOException;
import java.sql.*;
import java.util.List;


/**
 * 简单的jdbc连接hive实例(已开启kerberos服务)
 */
 
public class HiveSimple{
    /**
     * 用于连接Hive所需的一些参数设置 driverName:用于连接hive的JDBC驱动名 When connecting to
     * HiveServer2 with Kerberos authentication, the URL format is:
     * jdbc:hive2://:/;principal=
     * 
     */
    private static String driverName = "org.apache.hive.jdbc.HiveDriver";
    // 注意:这里的principal是固定不变的,其指的hive服务所对应的principal,而不是用户所对应的principal
    private static String url = "jdbc:hive2://gzhdp-nn03:9087/tenant_999;principal=aiinfo/[email protected];hive.server2.proxy.user=aiinfo";
    private static String sql = "";
    private static ResultSet res;
    private static Configuration conf = null;
    private static UserGroupInformation ugi = null;

    public static Connection get_conn() throws SQLException, ClassNotFoundException, IOException {

        /** 使用Hadoop安全登录 **/
          conf = new Configuration();
        // 这个hbase.keytab也是从远程服务器上copy下来的, 里面存储的是密码相关信息

        //System.setProperty("java.security.krb5.conf", "C:/Windows/krb5.ini");
        System.setProperty("java.security.krb5.kdc","gzhdp-nn01");
        System.setProperty("java.security.krb5.realm","BONC.COM");
        // 开启登陆调试日志
        System.setProperty("sun.security.krb5.debug", "true");

        conf.addResource(HDFSUtil.class.getResourceAsStream("/conf/core-site.xml"));
        conf.addResource(HDFSUtil.class.getResourceAsStream("/conf/hdfs-site.xml"));
        //conf.set("fs.hdfs.impl",org.apache.hadoop.hdfs.DistributedFileSystem.class.getName());
        conf.set("hadoop.security.authentication", "Kerberos");

       /* if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
            // 默认:这里不设置的话,win默认会到 C盘下读取krb5.init
            System.setProperty("java.security.krb5.conf", "E:/hivekey/krb5.conf");
        } // linux 会默认到 /etc/krb5.conf 中读取krb5.conf,本文笔者已将该文件放到/etc/目录下,因而这里便不用再设置了
        */

        try {
            UserGroupInformation.setConfiguration(conf);
            String user = Config.getObject("kerberos.principal");
            String path = Config.getObject("keytab.file");
            ugi = UserGroupInformation.loginUserFromKeytabAndReturnUGI(user, path);
            UserGroupInformation.setLoginUser(ugi);
        } catch (IOException e1) {
            e1.printStackTrace();
        }

        Class.forName(driverName);
        Connection conn = DriverManager.getConnection(url,"603ec1b520c0245bd92a3782b05e0b39","6567a0bd1b8f0921e1b823ccbdc9a820c385a60745e237c51086106d4b14673d");
        return conn;
    }
 
    /**
     * 查看数据库下所有的表
     *
     * @param statement
     * @return
     */
    public static boolean show_tables(Statement statement) {
        sql = "SHOW TABLES";
        System.out.println("Running:" + sql);
        try {
            ResultSet res = statement.executeQuery(sql);
            System.out.println("执行“+sql+运行结果:");
            while (res.next()) {
                System.out.println(res.getString(1));
            }
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }
 
    /**
     * 获取表的描述信息
     *
     * @param statement
     * @param tableName
     * @return
     */
    public static boolean describ_table(Statement statement, String tableName) {
        sql = "DESCRIBE " + tableName;
        try {
            res = statement.executeQuery(sql);
            System.out.print(tableName + "描述信息:");
            while (res.next()) {
                System.out.println(res.getString(1) + "\t" + res.getString(2));
            }
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }
 
    /**
     * 删除表
     *
     * @param statement
     * @param tableName
     * @return
     */
    public static boolean drop_table(Statement statement, String tableName) {
        sql = "DROP TABLE IF EXISTS " + tableName;
        System.out.println("Running:" + sql);
        try {
            statement.execute(sql);
            System.out.println(tableName + "删除成功");
            return true;
        } catch (SQLException e) {
            System.out.println(tableName + "删除失败");
            e.printStackTrace();
        }
        return false;
    }
 
    /**
     * 查看表数据
     *
     * @param preparedStatement
     * @return
     */
    public static boolean queryData(PreparedStatement preparedStatement, String tableName) {
        sql = "SELECT * FROM " + tableName + " LIMIT 10";
        System.out.println("Running:" + sql);
        try {
            res = preparedStatement.executeQuery(sql);
            System.out.println("执行“+sql+运行结果:");
            while (res.next()) {
                System.out.println(res.getString(1) + "," + res.getString(2) + "," + res.getString(3) + ","+"," + res.getString(12));

            }
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }
 
    /**
     * 创建表
     *
     * @param statement
     * @return
     */
    public static boolean createTable(Statement statement, String tableName) {
        sql = "CREATE TABLE test_1m_test2 AS SELECT * FROM test_1m_test"; //  为了方便直接复制另一张表数据来创建表
        System.out.println("Running:" + sql);
        try {
            boolean execute = statement.execute(sql);
            System.out.println("执行结果 :" + execute);
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;
    }
 
    public static void main(String[] args) {
 
        try {
            Connection conn = get_conn();
            ugi.checkTGTAndReloginFromKeytab();
            PreparedStatement stmt = conn.prepareStatement(sql);
            // 创建的表名
            String tableName = "user_info_360";
           // show_tables(stmt);
            queryData(stmt,tableName);
            // describ_table(stmt, tableName);
            /** 删除表 **/
            // drop_table(stmt, tableName);
            // show_tables(stmt);
            // queryData(stmt, tableName);
            //createTable(stmt, tableName);
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("!!!!!!END!!!!!!!!");
        }
    }
}

 

你可能感兴趣的:(大数据开发)