Java连接HBase的方法,包含Kerberos认证。
完整代码示例:
package com.example.hbase;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.io.compress.Compression.Algorithm;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.security.UserGroupInformation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class HBaseClientUtil {
private static final String TABLE_NAME = "test:test";
private static final String CF_DEFAULT = "cf";
/**
* HBase 通用客户端Kerberos认证
* @param resources 配置文件资源
* @param krb5Conf krb5.conf文件路径
* @param principal Kerberos用户主体,eg:[email protected]
* @param keytabFile keytab文件路径
* @return
* @throws IOException
*/
public static Connection getHBaseConn(List<String> resources, String krb5Conf, String principal, String keytabFile) throws IOException {
Configuration config = HBaseConfiguration.create();
// 添加必要的配置文件 (hbase-site.xml, core-site.xml)
for (int i = 0; i < resources.size(); i++) {
config.addResource(new Path(resources.get(i)));
}
// Kerberos认证
// 设置java安全krb5.conf
System.setProperty("java.security.krb5.conf", krb5Conf);
// 设置用户主体(Principal)
config.set("kerberos.principal" , principal);
// 使用用户keytab文件认证
config.set("keytab.file" , keytabFile);
UserGroupInformation.setConfiguration(config);
try {
// 登录
UserGroupInformation.loginUserFromKeytab(principal, keytabFile);
} catch (IOException e) {
e.printStackTrace();
}
// 创建连接
return ConnectionFactory.createConnection(config);
}
/**
* HBase2.2.0+ 客户端Kerberos认证 (未验证)
* @param resources 配置文件资源
* @param krb5Conf krb5.conf文件路径
* @param principal Kerberos用户主体,eg:[email protected]
* @param keytabFile keytab文件路径
* @return
* @throws IOException
*/
public static Connection getHBaseConn220(List<String> resources, String krb5Conf, String principal, String keytabFile) throws IOException {
Configuration config = HBaseConfiguration.create();
// 添加必要的配置文件 (hbase-site.xml, core-site.xml)
for (int i = 0; i < resources.size(); i++) {
config.addResource(new Path(resources.get(i)));
}
// 设置java安全krb5.conf (未验证是否需要此配置)
System.setProperty("java.security.krb5.conf", krb5Conf);
// Kerberos认证
config.set("hbase.client.kerberos.principal", principal);
config.set("hbase.client.keytab.file", keytabFile);
// 创建连接
return ConnectionFactory.createConnection(config);
}
public static void createOrOverwrite(Admin admin, HTableDescriptor table) throws IOException {
if (admin.tableExists(table.getTableName())) {
admin.disableTable(table.getTableName());
admin.deleteTable(table.getTableName());
}
admin.createTable(table);
}
public static void createSchemaTables(Connection connection) throws IOException {
try (Admin admin = connection.getAdmin()) {
HTableDescriptor table = new HTableDescriptor(TableName.valueOf(TABLE_NAME));
table.addFamily(new HColumnDescriptor(CF_DEFAULT).setCompressionType(Algorithm.NONE));
System.out.print("Creating table. ");
createOrOverwrite(admin, table);
System.out.println(" Done.");
}
}
public static void main(String... args) throws IOException {
// 添加必要的配置文件 (hbase-site.xml, core-site.xml)
List<String> resources = new ArrayList<String>() {
{
add("/home/xwd/ws/hbase/hbase-clientconfig/hbase-conf/core-site.xml");
add("/home/xwd/ws/hbase/hbase-clientconfig/hbase-conf/hbase-site.xml");
add("/home/xwd/ws/hbase/hbase-clientconfig/hbase-conf/hdfs-site.xml");
}
};
String krb5Conf = "/home/xwd/ws/hbase/krb5/krb5.conf";
String principal = "[email protected]";
String keytabFile = "/home/xwd/ws/hbase/krb5/xingweidong.keytab";
// HBase操作
Connection connection = HBaseClientUtil.getHBaseConn(resources, krb5Conf, principal, keytabFile);
createSchemaTables(connection);
System.out.println(" Put data ");
Table table = connection.getTable(TableName.valueOf("test:test"));
try {
Put put = new Put(Bytes.toBytes("hbase_client_test"));
put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("col"), Bytes.toBytes("hbase_loginUserFromKeytab"));
table.put(put);
} finally {
// 关闭连接
table.close();
connection.close();
}
}
}
从目标环境中复制即可,包括hadoop和hbase两个服务的相关jar。
如果是CDH集群,复制 /opt/cloudera/parcels/CDH/jars目录下的jar即可,里面包含了所有CDH服务的jar包。
如果使用IntelliJ IDEA开发,可以快速添加外部依赖库,以cdh依赖包为例。在工程中创建目录 src/libs/cdh,将依赖包放入cdh目录,右键点击cdh目录,选择 Add as Library。
在pom.xml指定外部库,就不需要从maven仓库进行下载了,也不会遇到版本问题,pom.xml示例:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.example.bigdatagroupId>
<artifactId>devexampleartifactId>
<version>1.0-SNAPSHOTversion>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<configuration>
<source>1.8source>
<target>1.8target>
<encoding>UTF-8encoding>
<compilerArguments>
<extdirs>${basedir}/src/libs/cdhextdirs>
compilerArguments>
configuration>
plugin>
plugins>
build>
project>
添加配置文件,加载目标环境的配置项。
Java通过HBase Client连接HBase的时候,可以加载目标环境的配置文件,以获取正确的配置,比起在代码中显示地设置配置项,加载配置文件资源的方法能够获取完整的目标环境配置,可以减少错误和精简代码。
主要配置文件是hbase-site.xml 和 core-site.xml,这里还添加了 hdfs-site.xml,为了避免缺少配置。这三个文件都是HBase的客户端配置下的文件。
获取HBase客户端配置的通用方式是从HBase的配置目录中获取。如果使用Cloudera Manager管理HBase,可以从Cloudera Manager中的HBase服务下载客户端配置:操作 -> 下载客户端配置。
如果HBase启用了Kerberos,在客户端访问时需要进行Kerberos认证,认证的方法有两种:
1、HBase通用客户端Kerberos认证。
2、HBase2.2.0+客户端Kerberos认证。从HBase 2.2.0开始支持的认证方法。
在 2.2.0 版本之前,客户端环境必须通过kinit
命令从 KDC 或 keytab 登录到 Kerberos,然后才能与 HBase 集群进行通信。
从 2.2.0 开始,客户端可以在hbase-site.xml
中指定以下配置:
<property>
<name>hbase.client.keytab.filename>
<value>/local/path/to/client/keytabvalue>
property>
<property>
<name>hbase.client.keytab.principalname>
<value>[email protected]value>
property>
然后,应用程序可以自动执行登录和凭据续订作业,而不会受到客户端干扰。
它是可选功能,客户端升级到 2.2.0,只要保持hbase.client.keytab.file
和hbase.client.keytab.principal
未设置,它仍然可以保留旧版本中的登录和凭证更新逻辑。
请注意,如果客户端和服务器端站点文件中的hbase.security.authentication
不匹配,则客户端将无法与群集通信。
示例代码:
/**
* HBase 通用客户端Kerberos认证
* @param resources 配置文件资源
* @param krb5Conf krb5.conf文件路径
* @param principal Kerberos用户主体,eg:[email protected]
* @param keytabFile keytab文件路径
* @return
* @throws IOException
*/
public static Connection getHBaseConn(List<String> resources, String krb5Conf, String principal, String keytabFile) throws IOException {
Configuration config = HBaseConfiguration.create();
// 添加必要的配置文件 (hbase-site.xml, core-site.xml)
for (int i = 0; i < resources.size(); i++) {
config.addResource(new Path(resources.get(i)));
}
// Kerberos认证
// 设置java安全krb5.conf
System.setProperty("java.security.krb5.conf", krb5Conf);
// 设置用户主体(Principal)
config.set("kerberos.principal" , principal);
// 使用用户keytab文件认证
config.set("keytab.file" , keytabFile);
UserGroupInformation.setConfiguration(config);
try {
// 登录
UserGroupInformation.loginUserFromKeytab(principal, keytabFile);
} catch (IOException e) {
e.printStackTrace();
}
// 创建连接
return ConnectionFactory.createConnection(config);
}
这个方法的核心是在代码中使用UserGroupInformation.loginUserFromKeytab()
进行Kerberos认证,主要配置说明:
配置 | 说明 |
---|---|
java.security.krb5.conf | krb5.conf文件路径。该文件从目标集群下载,文件在服务器的默认路径:/etc/krb5.conf。 |
keytab.file | 用户的keytab文件,使用keytab文件可以避免交互式输入密码。 |
kerberos.principal | Kerberos用户主体名。 |
UserGroupInformation.loginUserFromKeytab | 使用keytab方式登录。 |
上述示例代码主要描述了Kerberos认证,但是在实际应用中,Kerberos keytab是需要自动更新的,否则一旦keytab过期,应用就会出现认证失败的问题。
通过分析UserGroupInformation
的源码,发现了启用keytab自动更新的配置,主要源码如下:
private static synchronized void initialize(Configuration conf, boolean overrideNameRules) {
authenticationMethod = SecurityUtil.getAuthenticationMethod(conf);
if (overrideNameRules || !HadoopKerberosName.hasRulesBeenSet()) {
try {
HadoopKerberosName.setConfiguration(conf);
} catch (IOException var7) {
throw new RuntimeException("Problem with Kerberos auth_to_local name configuration", var7);
}
}
try {
kerberosMinSecondsBeforeRelogin = 1000L * conf.getLong("hadoop.kerberos.min.seconds.before.relogin", 60L);
} catch (NumberFormatException var6) {
throw new IllegalArgumentException("Invalid attribute value for hadoop.kerberos.min.seconds.before.relogin of " + conf.get("hadoop.kerberos.min.seconds.before.relogin"));
}
kerberosKeyTabLoginRenewalEnabled = conf.getBoolean("hadoop.kerberos.keytab.login.autorenewal.enabled", false);
if (!(groups instanceof UserGroupInformation.TestingGroups)) {
groups = Groups.getUserToGroupsMappingService(conf);
}
UserGroupInformation.conf = conf;
if (metrics.getGroupsQuantiles == null) {
int[] intervals = conf.getInts("hadoop.user.group.metrics.percentiles.intervals");
if (intervals != null && intervals.length > 0) {
int length = intervals.length;
MutableQuantiles[] getGroupsQuantiles = new MutableQuantiles[length];
for(int i = 0; i < length; ++i) {
getGroupsQuantiles[i] = metrics.registry.newQuantiles("getGroups" + intervals[i] + "s", "Get groups", "ops", "latency", intervals[i]);
}
metrics.getGroupsQuantiles = getGroupsQuantiles;
}
}
}
其中的kerberosKeyTabLoginRenewalEnabled
这个配置就是keytab自动更新的开关了,默认值为false,我们可以通过在应用代码中设置为true,来启用这个功能,示例代码如下:
// 启用keytab renewal
config.set("hadoop.kerberos.keytab.login.autorenewal.enabled", "true");
从HBase2.2.0版本开始,支持了一种新的Kerberos认证方法,只需要添加两个客户端配置,HBase会自动处理认证登录和凭证更新。
Hbase官方API org.apache.hadoop.hbase.client.ConnectionFactory
的说明如下:
Since 2.2.0, Connection created by ConnectionFactory can contain user-specified kerberos credentials if caller has following two configurations set:
- hbase.client.keytab.file, points to a valid keytab on the local filesystem
- hbase.client.kerberos.principal, gives the Kerberos principal to use
By this way, caller can directly connect to kerberized cluster without caring login and credentials renewal logic in application.
示例代码:(未验证)
/**
* HBase2.2.0+ 客户端Kerberos认证 (未验证)
* @param resources 配置文件资源
* @param krb5Conf krb5.conf文件路径
* @param principal Kerberos用户主体,eg:[email protected]
* @param keytabFile keytab文件路径
* @return
* @throws IOException
*/
public static Connection getHBaseConn220(List<String> resources, String krb5Conf, String principal, String keytabFile) throws IOException {
Configuration config = HBaseConfiguration.create();
// 添加必要的配置文件 (hbase-site.xml, core-site.xml)
for (int i = 0; i < resources.size(); i++) {
config.addResource(new Path(resources.get(i)));
}
// 设置java安全krb5.conf (未验证是否需要此配置)
System.setProperty("java.security.krb5.conf", krb5Conf);
// Kerberos认证
config.set("hbase.client.kerberos.principal", principal);
config.set("hbase.client.keytab.file", keytabFile);
// 创建连接
return ConnectionFactory.createConnection(config);
}
我使用的是CDH6.3.2,HBase版本是2.1.0,验证发现使用这两个配置项进行连接HBase是无法成功的,但是在Spark Streaming中使用这个配置可以成功将数据写入HBase,比较奇怪,也许有哪个地方不对。
使用目标用户登录gateway01.bigdata.zxxk.com主机,例如xingweidong,执行以下命令:
ipa-getkeytab -s utility1.bigdata.zxxk.com -p [email protected] -k ./xingweidong.keytab --password
输入密码即可获取keytab文件。
参数说明
参数 | 说明 | 示例值 |
---|---|---|
-s | FreeIPA服务端主机名 | utility1.bigdata.zxxk.com |
-p | 用户主体,注意加上域名 | [email protected] |
-k | keytab文件存储目录 | ./xingweidong.keytab |
–password | 使用密码 |
Caused by: javax.security.sasl.SaslException: Call to worker03.bigdata.zxxk.com/10.111.116.225:16020 failed on local exception: javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7) - LOOKING_UP_SERVER)] [Caused by javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7) - LOOKING_UP_SERVER)]]
...
Caused by: javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7) - LOOKING_UP_SERVER)]
...
Caused by: GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7) - LOOKING_UP_SERVER)
...
Caused by: KrbException: Server not found in Kerberos database (7) - LOOKING_UP_SERVER
...
Caused by: KrbException: Identifier doesn't match expected value (906)
...
问题:调用worker03.bigdata.zxxk.com/10.111.116.225:16020失败,在Kerberos数据库没有找到服务。
查看krb5kdc.log,发现:
Aug 08 09:56:41 utility1.bigdata.zxxk.com krb5kdc[11263](info): TGS_REQ (4 etypes {18 17 16 23}) 10.111.127.23: LOOKING_UP_SERVER: authtime 0, [email protected] for hbase/[email protected], Server not found in Kerberos database
kerberos在数据库中无法找到服务:hbase/[email protected]
但是kerberos数据库中存在服务:hbase/[email protected]
理论上,应该使用 hbase/[email protected] 访问hbase,但是不知道是哪个环节搞错了,所以无法正确访问hbase。
hbase客户端相关的配置如下:
<property>
<name>hbase.regionserver.kerberos.principalname>
<value>hbase/[email protected]value>
property>
这本案例中,正常来说,_HOST 会被替换为 worker03.bigdata.zxxk.com,但是根据结果来看,_HOST 被替换成 10.111.116.225 了。
经分析,替换错误的原因是DNS解析错误,只要在网络配置中配置正确的DNS服务即可解决。
在本案例中,配置DNS服务:114.114.114.114 和 8.8.8.8 ,可以正常访问HBase。