在数据源管理功能中,需要适配mysql、postgresql、hive等数据源。
mysql和postgresql连接方式一致,只需要驱动和jdbcurl即可,而hive背后是大数据集群,多采用Kerberos的方式保护集群环境,要想与大数据集群正常交互,需要经过kdc认证获取ticket,因此获取hive连接前需要先通过Kerberos认证
Java实现Kerberos认证
主要方法
1 2 | # 从keytab文件中加载用户标识并登录 org.apache.hadoop.security.UserGroupInformation#loginUserFromKeytab |
依赖
kerberos相关配置文件:krb5.conf、keytab文件从大数据集群管理员那获取
依赖:hive-jdbc:3.1.3(hive安装目录中带有该驱动包,可查看使用的版本)、hadoop-common:3.3.6,版本需和hive服务版本一致,在springboot3的框架下需要排除以下依赖,否则启动报错
1 | org.apache.hivehive-jdbc${hive.jdbc.version}HikariCP-java7com.zaxxerjavax.servlet.jsporg.glassfish.webjetty-runnerorg.eclipse.jettyorg.apache.hadoophadoop-common${hadoop.version}commons-lang3org.apache.commonscurator-clientorg.apache.curator |
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | String confPath = "xxxkrb5.conf" ; String keytabPath = "xxxhive.service.keytab" ; String principal = "hive/xxx@xxx" ; System.setProperty( "java.security.krb5.conf" , confPath); //System.setProperty("sun.security.krb5.debug", "true"); //System.setProperty("javax.security.auth.useSubjectCredsOnly", "false"); Configuration configuration = new Configuration(); configuration.set( "hadoop.security.authentication" , "KERBEROS" ); UserGroupInformation.setConfiguration(configuration); try { UserGroupInformation.loginUserFromKeytab(principal, keytabPath); } catch (IOException e) { log.error( "authKerberos exception" , e); throw new BizException( "kerberos认证失败" ); } |
除了上述方式外,也可采用JAAS可插拔的认证模块进行Kerberos认证
续期
Kerberos的ticket存在有效期,过期后导致服务不可用
Kerberos ticket存在两种有效期,ticket timelife(票据生命周期)、renewable lifetime(可再生周期)
- ticket的时间超过ticket lifetime时,该ticket将不可用
- 在renewable lifetime的时间内,可以对即将过期的ticket进行续期,超过renewable lifetime时间后,无法续期
- 例如kerb5.conf文件中的配置:ticket_lifetime = 24h renewable_lifetime = 7d,则在登录后的24小时内,可以对即将过期的ticket进行续期,距第一次登录7天后,将不再允许续期
UserGroupInformation提供了认证续期的私有方法UserGroupInformation#reloginFromKeytab(boolean)
,该方法有两个触发入口UserGroupInformation#checkTGTAndReloginFromKeytab
和UserGroupInformation#reloginFromKeytab()
值得注意的是
1 | UserGroupInformation#loginUserFromKeytab |
方法中会开启异步任务,定时触发续期方法,触发的续期方法即是
1 | UserGroupInformation#reloginFromKeytab() |
向线程池中提交续期的任务
也可以自定义触发续期的逻辑, 定期触发
1 | UserGroupInformation.getLoginUser().checkTGTAndReloginFromKeytab() |
连接hive
Kerberos认证之后,使用hive-jdbc连接hive,之后与连接mysql、pg的方式无异,使用DriverManager或数据源连接池Hikari皆可
遇到的问题
一开始选择hadoop-common
的版本是3.1.3
,执行Kerberos认证时报错
KerberosUtil
无法访问sun.security.krb5.Config
(它在java.security.jgss
模块中)。
分析
本项目采用的框架是spring boot3版本,最低要求的jdk版本是17,而Java在9之后引入了模块化机制,模块必须显式声明他们要到导出的包以供其他模块使用。
但是java.security.jgss
模块的module-info.class文件中并未声明
解决方式
1.在JVM的启动参数上增加以下参数
1 | --add-exports=java.security.jgss/sun.security.krb5=ALL-UNNAMED |
2.升级hadoop-common
版本到3.3.6
在该版本中KerberosUtil
并未通过反射访问sun.security.krb5.Config
-
hadoop-common:3.1.3
版本的KerberosUtil
hadoop-common:3.3.6
版本的KerberosUtil
扩展
利用JAAS机制认证Kerberos,不再使用UserGroupInformation
进行认证
增加一个配置文件gss-jaas.conf
1 2 3 4 5 6 7 8 9 10 11 | com.sun.security.jgss.initiate{ com.sun.security.auth.module.Krb5LoginModule required doNotPrompt= true useTicketCache= true useKeyTab= true renewTGT= true debug= true ticketCache= "/kerberos-tmp/krb5cc_1000" keyTab= "D:\xxxx\hive.service.keytab" principal= "hive/xxxx@xxxx" ; }; |
1 2 3 4 5 6 7 8 | private void authKerberos1() { // 指定gss-jaas.conf文件路径 System.setProperty( "java.security.auth.login.config" , "D:\xxx\gss-jaas.conf" ); // System.setProperty("sun.security.jgss.debug", "true"); System.setProperty( "javax.security.auth.useSubjectCredsOnly" , "false" ); String confPath = "D:\xxxx\krb5.conf" ; System.setProperty( "java.security.krb5.conf" , confPath); } |
思考
有时我们开发的系统是要部署到客户现场使用,而客户现场使用的hive版本和司内环境使用的版本可能不同。一般在程序开发时使用的依赖版本皆是定义在pom文件中,一旦打包之后依赖的版本就是固定的。因此需要考虑程序可适配不同的hive-jdbc版本,能够动态加载不同版本的hive-jdbc,或者在项目启动时可以指定额外的hive-jdbc驱动包
- 使用类加载器在程序运行时动态从指定路径读取并加载指定的驱动jar包
1 2 | URLClassLoader loader = new URLClassLoader(urls, Thread.currentThread().getContextClassLoader()); Class.forName(driverName, true , loader); |
- 在项目启动时能够将指定的目录中的jar包放到类路径中
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持IT俱乐部。