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 文件路径
* @param principal Kerberos用户主体,eg:xingweidong@
* @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();
// 添加必要的配置文件 (, )
for (int i = 0; i < resources.size(); i++) {
config.addResource(new Path(resources.get(i)));
}
// Kerberos认证
// 设置java安全
System.setProperty(".", krb5Conf);
// 设置用户主体(Principal)
config.set("" , principal);
// 使用用户keytab文件认证
config.set("" , 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 文件路径
* @param principal Kerberos用户主体,eg:xingweidong@
* @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();
// 添加必要的配置文件 (, )
for (int i = 0; i < resources.size(); i++) {
config.addResource(new Path(resources.get(i)));
}
// 设置java安全 (未验证是否需要此配置)
System.setProperty(".", krb5Conf);
// Kerberos认证
config.set("", principal);
config.set("", 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 {
// 添加必要的配置文件 (, )
List<String> resources = new ArrayList<String>() {
{
add("/home/xwd/ws/hbase/hbase-clientconfig/hbase-conf/");
add("/home/xwd/ws/hbase/hbase-clientconfig/hbase-conf/");
add("/home/xwd/ws/hbase/hbase-clientconfig/hbase-conf/");
}
};
String krb5Conf = "/home/xwd/ws/hbase/krb5/";
String principal = "xingweidong@";
String keytabFile = "/home/xwd/ws/hbase/krb5/";
// 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。
在指定外部库,就不需要从maven仓库进行下载了,也不会遇到版本问题,示例:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="/POM/4.0.0"
xmlns:xsi="http:///2001/XMLSchema-instance"
xsi:schemaLocation="/POM/4.0.0 /xsd/maven-4.0.">
<modelVersion>4.0.0</modelVersion>
<groupId></groupId>
<artifactId>devexample</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId></groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<compilerArguments>
<extdirs>${basedir}/src/libs/cdh</extdirs><!--指定外部lib-->
</compilerArguments>
</configuration>
</plugin>
</plugins>
</build>
</project>
添加配置文件
添加配置文件,加载目标环境的配置项。
Java通过HBase Client连接HBase的时候,可以加载目标环境的配置文件,以获取正确的配置,比起在代码中显示地设置配置项,加载配置文件资源的方法能够获取完整的目标环境配置,可以减少错误和精简代码。
主要配置文件是 和 ,这里还添加了 ,为了避免缺少配置。这三个文件都是HBase的客户端配置下的文件。
获取HBase客户端配置的通用方式是从HBase的配置目录中获取。如果使用Cloudera Manager管理HBase,可以从Cloudera Manager中的HBase服务下载客户端配置:操作 -> 下载客户端配置。
Kerberos认证
如果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 开始,客户端可以在中指定以下配置:
<property>
<name></name>
<value>/local/path/to/client/keytab</value>
</property>
<property>
<name></name>
<value>foo@</value>
</property>
然后,应用程序可以自动执行登录和凭据续订作业,而不会受到客户端干扰。
它是可选功能,客户端升级到 2.2.0,只要保持和
未设置,它仍然可以保留旧版本中的登录和凭证更新逻辑。
请注意,如果客户端和服务器端站点文件中的不匹配,则客户端将无法与群集通信。
HBase通用客户端Kerberos认证
示例代码:
/**
* HBase 通用客户端Kerberos认证
* @param resources 配置文件资源
* @param krb5Conf 文件路径
* @param principal Kerberos用户主体,eg:xingweidong@
* @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();
// 添加必要的配置文件 (, )
for (int i = 0; i < resources.size(); i++) {
config.addResource(new Path(resources.get(i)));
}
// Kerberos认证
// 设置java安全
System.setProperty(".", krb5Conf);
// 设置用户主体(Principal)
config.set("" , principal);
// 使用用户keytab文件认证
config.set("" , keytabFile);
UserGroupInformation.setConfiguration(config);
try {
// 登录
UserGroupInformation.loginUserFromKeytab(principal, keytabFile);
} catch (IOException e) {
e.printStackTrace();
}
// 创建连接
return ConnectionFactory.createConnection(config);
}
这个方法的核心是在代码中使用()
进行Kerberos认证,主要配置说明:
配置 | 说明 |
---|---|
. | 文件路径。该文件从目标集群下载,文件在服务器的默认路径:/etc/。 |
用户的keytab文件,使用keytab文件可以避免交互式输入密码。 | |
Kerberos用户主体名。 | |
使用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("", 60L);
} catch (NumberFormatException var6) {
throw new IllegalArgumentException("Invalid attribute value for of " + conf.get(""));
}
kerberosKeyTabLoginRenewalEnabled = conf.getBoolean("", false);
if (!(groups instanceof UserGroupInformation.TestingGroups)) {
groups = Groups.getUserToGroupsMappingService(conf);
}
UserGroupInformation.conf = conf;
if (metrics.getGroupsQuantiles == null) {
int[] intervals = conf.getInts("");
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("", "true");
HBase2.2.0+客户端Kerberos认证
从HBase2.2.0版本开始,支持了一种新的Kerberos认证方法,只需要添加两个客户端配置,HBase会自动处理认证登录和凭证更新。
Hbase官方API 的说明如下:
Since 2.2.0, Connection created by ConnectionFactory can contain user-specified kerberos credentials if caller has following two configurations set:
- , points to a valid keytab on the local filesystem
- , 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 文件路径
* @param principal Kerberos用户主体,eg:xingweidong@
* @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();
// 添加必要的配置文件 (, )
for (int i = 0; i < resources.size(); i++) {
config.addResource(new Path(resources.get(i)));
}
// 设置java安全 (未验证是否需要此配置)
System.setProperty(".", krb5Conf);
// Kerberos认证
config.set("", principal);
config.set("", keytabFile);
// 创建连接
return ConnectionFactory.createConnection(config);
}
我使用的是CDH6.3.2,HBase版本是2.1.0,验证发现使用这两个配置项进行连接HBase是无法成功的,但是在Spark Streaming中使用这个配置可以成功将数据写入HBase,比较奇怪,也许有哪个地方不对。
获取keytab文件
使用目标用户登录主机,例如xingweidong,执行以下命令:
ipa-getkeytab -s -p xingweidong@ -k ./ --password
输入密码即可获取keytab文件。
参数说明
参数 | 说明 | 示例值 |
---|---|---|
-s | FreeIPA服务端主机名 | |
-p | 用户主体,注意加上域名 | xingweidong@ |
-k | keytab文件存储目录 | ./ |
–password | 使用密码 |
问题记录
Server not found in Kerberos database
报错
Caused by: : Call to /10.111.116.225:16020 failed on local exception: : GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7) - LOOKING_UP_SERVER)] [Caused by : GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Server not found in Kerberos database (7) - LOOKING_UP_SERVER)]]
...
Caused by: : 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)
...
问题:调用/10.111.116.225:16020失败,在Kerberos数据库没有找到服务。
分析
查看,发现:
Aug 08 09:56:41 krb5kdc[11263](info): TGS_REQ (4 etypes {18 17 16 23}) 10.111.127.23: LOOKING_UP_SERVER: authtime 0, hbase_zxxk_user@ for hbase/10.111.116.225@, Server not found in Kerberos database
kerberos在数据库中无法找到服务:hbase/10.111.116.225@
但是kerberos数据库中存在服务:hbase/@
理论上,应该使用 hbase/@ 访问hbase,但是不知道是哪个环节搞错了,所以无法正确访问hbase。
解决
hbase客户端相关的配置如下:
<property>
<name></name>
<value>hbase/_HOST@</value>
</property>
这本案例中,正常来说,_HOST 会被替换为 ,但是根据结果来看,_HOST 被替换成 10.111.116.225 了。
经分析,替换错误的原因是DNS解析错误,只要在网络配置中配置正确的DNS服务即可解决。
在本案例中,配置DNS服务:114.114.114.114 和 8.8.8.8 ,可以正常访问HBase。