hive0.11 hiveserver custom认证bug

本文涉及的产品
运维安全中心(堡垒机),免费版 6个月
运维安全中心(堡垒机),企业双擎版 50资产 7天
简介:

 最近在测试hive0.11 hiveserver时遇到的一个关于认证的bug,具体表现:
在配置中指定了custom的认证方式时,通过beeline连接hiveserver2,发现连接hang住。
hive配置:

1
2
3
4
5
6
7
8
<property>
<name>hive.server2.authentication</name>a
<value>CUSTOM</value>
</property>
<property>
<name>hive.server2.custom.authentication. class </name>
<value>com.vipshop.hive.service.AuthWithPasswd</value>
</property>

查看hiveserver的日志,发现有如下报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
15 / 01 / 08  17 : 54 : 59  ERROR server.TThreadPoolServer: Error occurred during processing of message.
java.lang.RuntimeException: java.lang.NoSuchMethodException: org.apache.hive.service.auth.PasswdAuthenticationProvider.<init>()
         at org.apache.hadoop.util.ReflectionUtils.newInstance(ReflectionUtils.java: 131 )
         at org.apache.hive.service.auth.CustomAuthenticationProviderImpl.<init>(CustomAuthenticationProviderImpl.java: 52 )
         at org.apache.hive.service.auth.AuthenticationProviderFactory.getAuthenticationProvider(AuthenticationProviderFactory.java: 62 )
         at org.apache.hive.service.auth.PlainSaslHelper$PlainServerCallbackHandler.handle(PlainSaslHelper.java: 73 )
         at org.apache.hive.service.auth.PlainSaslServer.evaluateResponse(PlainSaslServer.java: 102 )
         at org.apache.thrift.transport.TSaslTransport$SaslParticipant.evaluateChallengeOrResponse(TSaslTransport.java: 509 )
         at org.apache.thrift.transport.TSaslTransport.open(TSaslTransport.java: 264 )
         at org.apache.thrift.transport.TSaslServerTransport.open(TSaslServerTransport.java: 41 )
         at org.apache.thrift.transport.TSaslServerTransport$Factory.getTransport(TSaslServerTransport.java: 216 )
         at org.apache.thrift.server.TThreadPoolServer$WorkerProcess.run(TThreadPoolServer.java: 189 )
         at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java: 886 )
         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java: 908 )
         at java.lang.Thread.run(Thread.java: 662 )
Caused by: java.lang.NoSuchMethodException: org.apache.hive.service.auth.PasswdAuthenticationProvider.<init>()
         at java.lang.Class.getConstructor0(Class.java: 2706 )
         at java.lang.Class.getDeclaredConstructor(Class.java: 1985 )
         at org.apache.hadoop.util.ReflectionUtils.newInstance(ReflectionUtils.java: 125 )
         ...  12  more

注释掉hive.server2.authentication的配置(即使用默认的none)后正常。这其实是hive0.11.0中的bug,在hive0.13.0中fix,bug id:

https://issues.apache.org/jira/browse/HIVE-4778
下面分析下具体涉及的类:
org.apache.hive.service.auth.HiveAuthFactory中的AuthTypes定义了多种认证方法(NONE,LDAP,KERBEROS,CUSTOM,PAM(hive0.11不支持)等)这里我们用到了CUSTOM,基于user和password的认证方式,涉及到PasswdAuthenticationProvider和CustomAuthenticationProviderImpl类

1)org.apache.hive.service.auth.PasswdAuthenticationProvider是一个基于用户名和密码验证的接口,主要定义了一个抽象方法Authenticate (参数就是用户名和密码)
2)org.apache.hive.service.auth.AnonymousAuthenticationProviderImpl类实现了PasswdAuthenticationProvider接口,提供了一个空的Authenticate方法(直接return)
3)org.apache.hive.service.auth.AuthenticationProviderFactory定义了一个AuthMethods的enum类,定义了有效的认证方法(hive0.13:LDAP/PAM/CUSTOM/NONE,hive0.11:LDAP/CUSTOM/NONE),并提供了对应的封装,同时定义了一个getAuthenticationProvider方法,用来返回对应的 AuthMethods的具体实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   public  static  PasswdAuthenticationProvider getAuthenticationProvider(AuthMethods authMethod)
       throws  AuthenticationException {
     if  (authMethod.equals(AuthMethods.LDAP)) {
       return  new  LdapAuthenticationProviderImpl();
     }
     else  if  (authMethod.equals(AuthMethods.PAM)) {
       return  new  PamAuthenticationProviderImpl();
     }
     else  if  (authMethod.equals(AuthMethods.CUSTOM)) {
       return  new  CustomAuthenticationProviderImpl();
     }
     else  if  (authMethod.equals(AuthMethods.NONE)) {
       return  new  AnonymousAuthenticationProviderImpl();
     }
     else  {
       throw  new  AuthenticationException( "Unsupported authentication method" );
     }
   }

4)org.apache.hive.service.auth.CustomAuthenticationProviderImpl
PasswdAuthenticationProvider接口的具体实现类
在hive0.13中的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public  class  CustomAuthenticationProviderImpl  implements  PasswdAuthenticationProvider {
   Class<?  extends  PasswdAuthenticationProvider> customHandlerClass;
   PasswdAuthenticationProvider customProvider;
   @SuppressWarnings ( "unchecked" )
   CustomAuthenticationProviderImpl () {
     HiveConf conf =  new  HiveConf();
     this .customHandlerClass = (Class<?  extends  PasswdAuthenticationProvider>)
         conf.getClass( 
             HiveConf.ConfVars.HIVE_SERVER2_CUSTOM_AUTHENTICATION_CLASS.varname,  //即hive.server2.custom.authentication.class
             PasswdAuthenticationProvider. class );
     //HIVE_SERVER2_CUSTOM_AUTHENTICATION_CLASS("hive.server2.custom.authentication.class", null),
     this .customProvider =
         ReflectionUtils.newInstance( this .customHandlerClass, conf);  //反射调用,生成class的实例
   }
   @Override
   public  void  Authenticate(String user, String  password)  //实现具体的Authenticate方法,用于实现的验证
       throws  AuthenticationException {
     this .customProvider.Authenticate(user, password);
   }
}

hive0.11中的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public  class  CustomAuthenticationProviderImpl
   implements  PasswdAuthenticationProvider {
   Class<?  extends  PasswdAuthenticationProvider> customHandlerClass;
   PasswdAuthenticationProvider customProvider;
   @SuppressWarnings ( "unchecked" )
   CustomAuthenticationProviderImpl () {
     HiveConf conf =  new  HiveConf();
     this .customHandlerClass = (Class<?  extends  PasswdAuthenticationProvider>)
         conf.getClass(
             HiveConf.ConfVars.HIVE_SERVER2_CUSTOM_AUTHENTICATION_CLASS.name(),  //即HIVE_SERVER2_CUSTOM_AUTHENTICATION_CLASS
             PasswdAuthenticationProvider. class );
     this .customProvider =
         ReflectionUtils.newInstance( this .customHandlerClass, conf);
   }
   @Override
   public  void  Authenticate(String user, String  password)
       throws  AuthenticationException {
     this .customProvider.Authenticate(user, password);
   }
}

不同的就是customHandlerClass 的获取方法,这里我们手动测试下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import  java.lang.*;
public  class  HiveConf{
   public  static  enum  ConfVars {
     PLAN_SERIALIZATION( "hive.plan.serialization.format" , "kryo" ),
     HIVE_SERVER2_CUSTOM_AUTHENTICATION_CLASS( "hive.server2.custom.authentication.class" null ),
     ;
     public  final  String varname;
     public  final  String defaultVal;
     ConfVars(String varname, String defaultVal) {
       this .varname = varname;
       this .defaultVal = defaultVal;
     }
     public  String toString() {
       return  varname;
     }
     }
     public  static  void  main(String args[]) {
         System.out.println( "ConfVars List:" );
         for (ConfVars c:ConfVars.values()){  //ConfVars.values()是所有enum的值
             System.out.println(c  +  " is: "  + c);
//hive.server2.custom.authentication.class is: hive.server2.custom.authentication.class
             System.out.println(c  +  " varname: "  + c.varname);  //可以看到这里varname是enum元素定义的值的名称,而name()是enum元素的名称
//hive.server2.custom.authentication.class varname: hive.server2.custom.authentication.class
             System.out.println(c  +  " name(): "  + c.name());
//hive.server2.custom.authentication.class name(): HIVE_SERVER2_CUSTOM_AUTHENTICATION_CLASS
         }
         System.out.println(HiveConf.ConfVars.HIVE_SERVER2_CUSTOM_AUTHENTICATION_CLASS.name());  //HIVE_SERVER2_CUSTOM_AUTHENTICATION_CLASS
         System.out.println(HiveConf.ConfVars.HIVE_SERVER2_CUSTOM_AUTHENTICATION_CLASS.varname);  //hive.server2.custom.authentication.class
     }
}

可以看到使用HiveConf.ConfVars.HIVE_SERVER2_CUSTOM_AUTHENTICATION_CLASS.varname时,this.customHandlerClass的结果为具体的实现类(即hive.server2.custom.authentication.class设置的类)比如class com.vipshop.hive.service.AuthWithPasswd,而使用HiveConf.ConfVars.HIVE_SERVER2_CUSTOM_AUTHENTICATION_CLASS.name()时,返回的是接口,即interface org.apache.hive.service.auth.PasswdAuthenticationProvider
再来看ReflectionUtils.newInstance方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private  static  final  Class<?>[] EMPTY_ARRAY =  new  Class[]{};
....
   public  static  <T> T newInstance(Class<T> theClass, Configuration conf) {
     T result;
     try  {
       Constructor<T> meth = (Constructor<T>) CONSTRUCTOR_CACHE.get(theClass);
       if  (meth ==  null ) {
         meth = theClass.getDeclaredConstructor(EMPTY_ARRAY);   //返回类的构造函数对象
         meth.setAccessible( true );
         CONSTRUCTOR_CACHE.put(theClass, meth);
       }
       result = meth.newInstance();
     catch  (Exception e) {
       throw  new  RuntimeException(e);
     }
     setConf(result, conf);
     return  result;
   }

在hive0.11时这里theClass为org.apache.hive.service.auth.PasswdAuthenticationProvider,org.apache.hive.service.auth.PasswdAuthenticationProvider是一个接口,没有定义构造函数,因此会抛出异常。
再来看认证的配置是在什么时候加载的?我们通过指定一个错误的配置来看其报错堆栈:
使用如下命令开启hiveserver的debug log:

1
bin/hiveserver2 -hiveconf hive.root.logger=DEBUG,console start
1
2
3
4
5
6
7
15 / 01 / 08  17 : 56 : 55  INFO service.AbstractService: Service:ThriftBinaryCLIService is started.
15 / 01 / 08  17 : 56 : 55  INFO service.AbstractService: Service:HiveServer2 is started.
15 / 01 / 08  17 : 56 : 55  ERROR thrift.ThriftCLIService: Error: 
javax.security.auth.login.LoginException: Unsupported authentication type CUSTEM
         at org.apache.hive.service.auth.HiveAuthFactory.getAuthTransFactory(HiveAuthFactory.java: 148 )
         at org.apache.hive.service.cli.thrift.ThriftBinaryCLIService.run(ThriftBinaryCLIService.java: 43 )
         at java.lang.Thread.run(Thread.java: 662 )

在hiveserver2正常启动时默认会启动ThriftBinaryCLIService服务:
在ThriftBinaryCLIService 类的run方法:

1
2
3
4
5
6
7
   public  void  run() {
     try  {
       hiveAuthFactory =  new  HiveAuthFactory();   //run方法中首先会声明一个HiveAuthFactory对象
       TTransportFactory  transportFactory = hiveAuthFactory.getAuthTransFactory();  //调用HiveAuthFactory的getAuthTransFactory方法获取
对应的TTransportFactory对象  
       TProcessorFactory processorFactory = hiveAuthFactory.getAuthProcFactory( this );
.....

而在HiveAuthFactory的构造函数中会解析hive的配置,获取对应的hiveserver的认证设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
   public  HiveAuthFactory()  throws  TTransportException {
     conf =  new  HiveConf();
     transportMode = conf.getVar(HiveConf.ConfVars.HIVE_SERVER2_TRANSPORT_MODE);
//HIVE_SERVER2_TRANSPORT_MODE("hive.server2.transport.mode", "binary",new StringsValidator("binary", "http")),
     authTypeStr = conf.getVar(HiveConf.ConfVars.HIVE_SERVER2_AUTHENTICATION);
//HIVE_SERVER2_AUTHENTICATION("hive.server2.authentication", "NONE", new StringsValidator("NOSASL", "NONE", "LDAP", "KERBEROS", "PAM", "CUSTOM")),
默认为 null ,有效值为 "NOSASL" "NONE" "LDAP" "KERBEROS" "PAM" "CUSTOM"
     // In http mode we use NOSASL as the default auth type
     if  (transportMode.equalsIgnoreCase( "http" )) {
       if  (authTypeStr ==  null ) {
         authTypeStr = AuthTypes.NOSASL.getAuthName();
       }
     }
     else  {
       if  (authTypeStr ==  null ) {
         authTypeStr = AuthTypes.NONE.getAuthName();
       }
       if  (authTypeStr.equalsIgnoreCase(AuthTypes.KERBEROS.getAuthName())
           && ShimLoader.getHadoopShims().isSecureShimImpl()) {
         saslServer = ShimLoader.getHadoopThriftAuthBridge().createServer(
             conf.getVar(ConfVars.HIVE_SERVER2_KERBEROS_KEYTAB),
             conf.getVar(ConfVars.HIVE_SERVER2_KERBEROS_PRINCIPAL)
             );
         // start delegation token manager
         try  {
           saslServer.startDelegationTokenSecretManager(conf,  null );
         catch  (IOException e) {
           throw  new  TTransportException( "Failed to start token manager" , e);
         }
       }
     }
   }

getAuthTransFactory方法会判断authTypeStr是否为有效值,否则抛出异常,退出启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   public  TTransportFactory getAuthTransFactory()  throws  LoginException {
     TTransportFactory transportFactory;
     if  (authTypeStr.equalsIgnoreCase(AuthTypes.KERBEROS.getAuthName())) {
       try  {
         transportFactory = saslServer.createTransportFactory(getSaslProperties());
       catch  (TTransportException e) {
         throw  new  LoginException(e.getMessage());
       }
     else  if  (authTypeStr.equalsIgnoreCase(AuthTypes.NONE.getAuthName())) {
       transportFactory = PlainSaslHelper.getPlainTransportFactory(authTypeStr);
     else  if  (authTypeStr.equalsIgnoreCase(AuthTypes.LDAP.getAuthName())) {
       transportFactory = PlainSaslHelper.getPlainTransportFactory(authTypeStr);
     else  if  (authTypeStr.equalsIgnoreCase(AuthTypes.PAM.getAuthName())) {
       transportFactory = PlainSaslHelper.getPlainTransportFactory(authTypeStr);
     else  if  (authTypeStr.equalsIgnoreCase(AuthTypes.NOSASL.getAuthName())) {
       transportFactory =  new  TTransportFactory();
     else  if  (authTypeStr.equalsIgnoreCase(AuthTypes.CUSTOM.getAuthName())) {
       transportFactory = PlainSaslHelper.getPlainTransportFactory(authTypeStr);
     else  {
       throw  new  LoginException( "Unsupported authentication type "  + authTypeStr);
     }
     return  transportFactory;
   }


本文转自菜菜光 51CTO博客,原文链接:http://blog.51cto.com/caiguangguang/1602206,如需转载请自行联系原作者
相关文章
|
SQL 安全 Java
一篇文章彻底理解 HIVE 常见的三种 AUTHENTICATION 认证机制的配置与使用
一篇文章彻底理解 HIVE 常见的三种 AUTHENTICATION 认证机制的配置与使用
|
SQL 分布式计算 HIVE
Hive Cli / HiveServer2 中使用 dayofweek 函数引发的BUG!
在Hive 3.1.2和Spark 3.0.2集群环境中,遇到`dayofweek`函数bug。当`create_date`为字符串类型时,`dayofweek`函数结果错位。修复方法是将`create_date`转换为`date`类型。在Spark SQL中,原始代码能正常运行,未出现此问题。因此建议在Hive中使用转换后的日期类型以避免错误。
282 4
|
SQL 关系型数据库 MySQL
实时计算 Flink版产品使用问题之如何使用Flink SQL连接带有Kerberos认证的Hive
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
SQL 消息中间件 Kafka
flink 读取kafka 写入带kerberos认证的hive环境
flink 读取kafka 写入带kerberos认证的hive环境
|
SQL 分布式计算 数据管理
聊聊Hive数据血缘——从Atlas没有列级血缘的Bug讲起
聊聊Hive数据血缘——从Atlas没有列级血缘的Bug讲起
559 0
|
SQL 分布式计算 资源调度
大数据问题排查系列-大数据集群开启 kerberos 认证后 HIVE 作业执行失败
大数据问题排查系列-大数据集群开启 kerberos 认证后 HIVE 作业执行失败
|
SQL 分布式计算 Java
hive metastore配置kerberos认证
hive从3.0.0开始提供hive metastore单独服务作为像presto、flink、spark等组件的元数据中心。但是默认情况下hive metastore在启动之后是不需要进行认证就可以访问的。所以本文基于大数据组件中流行的kerberos认证方式,对hive metastore进行认证配置。
|
SQL 分布式计算 Java