window 環境下jdbc訪問啓用kerberos的impala

最近,公司生產集羣添加kerberos安全認證後,訪問集羣的任何組件都須要進行認證,這樣問題來了,對於impala,未配置kerberos安全認證以前經過impala的jdbc驅動(impala-jdbc),配置鏈接字符串java

jdbc:impala://192.168.1.10:21050/default;AuthMech=3;UID=user;PWD=pwd;UseSasl=0

是能夠正常訪問的,可是開啓了kerberos後,impala訪問報錯,通過閱讀impala jdbc使用文檔(https://www.cloudera.com/documentation/other/connectors/impala-jdbc/2-5-5/Cloudera-JDBC-Driver-for-Impala-Install-Guide-2-5-5.pdf),能夠發現,對於啓用kerberos的impala鏈接字符串須要調整爲以下:sql

jdbc:impala://192.168.1.10:21050/default;AuthMech=1;KrbHostFQDN=hostalias;KrbServiceName=impala

其中:KrbHostFQDN須要指定鏈接哪臺服務器的impalad,須要使用服務器的別名。apache

調整jdbc配置,鏈接impala失敗,異常信息以下api

java.sql.SQLException: [Simba][ImpalaJDBCDriver](500168) Error creating login context using ticket cache: Unable to obtain Principal Name for authentication .
 ......
Caused by: com.cloudera.support.exceptions.GeneralException: [Simba][ImpalaJDBCDriver](500168) Error creating login context using ticket cache: Unable to obtain Principal Name for authentication .
    ... 39 more
Caused by: javax.security.auth.login.LoginException: Unable to obtain Principal Name for authentication 
    at com.sun.security.auth.module.Krb5LoginModule.promptForName(Krb5LoginModule.java:841)
    at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:704)
    at com.sun.security.auth.module.Krb5LoginModule.login(Krb5LoginModule.java:617)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at javax.security.auth.login.LoginContext.invoke(LoginContext.java:755)
    at javax.security.auth.login.LoginContext.access$000(LoginContext.java:195)
    at javax.security.auth.login.LoginContext$4.run(LoginContext.java:682)
    at javax.security.auth.login.LoginContext$4.run(LoginContext.java:680)
    at java.security.AccessController.doPrivileged(Native Method)
    at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:680)
    at javax.security.auth.login.LoginContext.login(LoginContext.java:587)
    at com.cloudera.jdbc.kerberos.Kerberos.getSubjectViaTicketCache(Unknown Source)
    at com.cloudera.hivecommon.api.HiveServer2ClientFactory.createTransport(Unknown Source)
    at com.cloudera.hivecommon.api.HiveServer2ClientFactory.createClient(Unknown Source)
    at com.cloudera.hivecommon.core.HiveJDBCCommonConnection.connect(Unknown Source)
    at com.cloudera.impala.core.ImpalaJDBCConnection.connect(Unknown Source)
    at com.cloudera.jdbc.common.BaseConnectionFactory.doConnect(Unknown Source)
    at com.cloudera.jdbc.common.AbstractDriver.connect(Unknown Source)
    at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1421)
    at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1477)
    at com.alibaba.druid.pool.DruidDataSource.init(DruidDataSource.java:677)
View Code

 

  經過上述異常信息可知,kerberos認證未經過。因爲是window環境,不屬於hadoop安全範圍,因此認證失敗,爲了認證經過,須要以下的代碼:安全

@Test
    /**
     * 鏈接Impala查詢
     */
    public void testImpala() throws SQLException, IOException {
        Configuration conf = new Configuration();
        conf.set("hadoop.security.authentication", "Kerberos");
        UserGroupInformation.setConfiguration(conf);
        UserGroupInformation.loginUserFromKeytab(user, keyTabPath);
        UserGroupInformation loginUser = UserGroupInformation.getLoginUser();
        String query = "select * from table limit 1";
        loginUser.doAs((PrivilegedAction<Void>) () -> {
            try {
                try (Connection connection = DriverManager.getConnection(impalaUrl)) {
                    try (Statement statement = connection.createStatement()) {
                        ResultSet resultSet = statement.executeQuery(query);
                        while (resultSet.next()) {
                            System.out.println(resultSet.getObject(1));
                        }
                    }
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return null;
        });
    }
View Code

 

 

首先,設置hadoop.security.authentication爲kerberos,其次,使用UserGroupInformation來進行操做。這樣雖然kerberos認證是經過了,可是仍是有其餘異常:服務器

java.lang.IllegalArgumentException: Can't get Kerberos realm
	....
Caused by: java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.apache.hadoop.security.authentication.util.KerberosUtil.getDefaultRealm(KerberosUtil.java:84)
	at org.apache.hadoop.security.HadoopKerberosName.setConfiguration(HadoopKerberosName.java:63)
	... 30 more
Caused by: KrbException: Cannot locate default realm
	at sun.security.krb5.Config.getDefaultRealm(Config.java:1029)
	... 36 more

  沒法獲取Kerberos realm,具體出現這個問題的地方在sun.security.krb5.Config文件中,閱讀該Config類的構造函數,會發現以下代碼:ide

Config(){
.....
 try {
                String var3 = this.getJavaFileName();
                List var2;
                if(var3 != null) {
                    var2 = this.loadConfigFile(var3);
                    this.stanzaTable = this.parseStanzaTable(var2);
                    if(DEBUG) {
                        System.out.println("Loaded from Java config");
                    }
                } else {
                    boolean var4 = false;
                    if(isMacosLionOrBetter()) {
                        try {
                            this.stanzaTable = SCDynamicStoreConfig.getConfig();
                            if(DEBUG) {
                                System.out.println("Loaded from SCDynamicStoreConfig");
                            }

                            var4 = true;
                        } catch (IOException var6) {
                            ;
                        }
                    }

                    if(!var4) {
                        var3 = this.getNativeFileName();
                        var2 = this.loadConfigFile(var3);
                        this.stanzaTable = this.parseStanzaTable(var2);
                        if(DEBUG) {
                            System.out.println("Loaded from native config");
                        }
                    }
                }
            } catch (IOException var7) {
                ;
            }
}

private String getJavaFileName() {
        String var1 = getProperty("java.security.krb5.conf");
        if(var1 == null) {
            var1 = getProperty("java.home") + File.separator + "lib" + File.separator + "security" + File.separator + "krb5.conf";
            if(!this.fileExists(var1)) {
                var1 = null;
            }
        }

        if(DEBUG) {
            System.out.println("Java config name: " + var1);
        }

        return var1;
    }

 private String getNativeFileName() {
        String var1 = null;
        String var2 = getProperty("os.name");
        if(var2.startsWith("Windows")) {
            try {
                Credentials.ensureLoaded();
            } catch (Exception var4) {
                ;
            }

            if(Credentials.alreadyLoaded) {
                String var3 = getWindowsDirectory(false);
                if(var3 != null) {
                    if(var3.endsWith("\\")) {
                        var3 = var3 + "krb5.ini";
                    } else {
                        var3 = var3 + "\\krb5.ini";
                    }

                    if(this.fileExists(var3)) {
                        var1 = var3;
                    }
                }

                if(var1 == null) {
                    var3 = getWindowsDirectory(true);
                    if(var3 != null) {
                        if(var3.endsWith("\\")) {
                            var3 = var3 + "krb5.ini";
                        } else {
                            var3 = var3 + "\\krb5.ini";
                        }

                        var1 = var3;
                    }
                }
            }

            if(var1 == null) {
                var1 = "c:\\winnt\\krb5.ini";
            }
        } else if(var2.startsWith("SunOS")) {
            var1 = "/etc/krb5/krb5.conf";
        } else if(var2.contains("OS X")) {
            var1 = this.findMacosConfigFile();
        } else {
            var1 = "/etc/krb5.conf";
        }

        if(DEBUG) {
            System.out.println("Native config name: " + var1);
        }

        return var1;
    }
View Code

 

  其中重要的就是getJavaFileName和getNativeFileName兩個方法,getJavaFileName中會查找java.security.krb5.conf屬性,而getNativeFileName會查找系統目錄(對於window就是C:\Windows目錄)。函數

因此,在window環境下,能夠在系統屬性中配置上java.security.krb5.conf屬性來指定krb5.ini文件,或將krb5.conf 保存到%JAVA_HOME%\lib\security\目錄下,或將krb5.ini文件保存到c:\window目錄下便可正常訪問kerberos安全認證的Impala。oop

 

  程序調試kerberos異常信息,啓動的時候設置「-Dsun.security.krb5.debug=true」 ,再次啓動就會輸出kerberos的詳細信息,根據詳細信息進行排查。 測試

         

       截圖的異常信息是開啓了kerberos的調試信息後打印的信息。其中「Clock skew too great」說明系統時間與kerberos系統的時間差距超出了5分鐘。

 

 

測試環境:

客戶端操做系統: window 7

JDK:1.8

Impala-jdbc:

 <dependency>
       <groupId>com.cloudera.impala</groupId>
       <artifactId>impala-jdbc</artifactId>
       <version>2.5.34-cdh5.7.2</version>
 </dependency>

服務器環境:

操做系統:Centos 6.5

CDH:5.7.2

相關文章
相關標籤/搜索