本问记录使用JSch登录sftps时遇到的Kerberos验证问题并记录了解决方法
项目场景:
项目开发中使用了SFTP,debug调试程序时发现了每次都需要手动输入 Kerberos的口令信息。这就很奇怪了难道每次连接SFTP时候都需要手动输入吗?
日志如下:
2023-06-26 17:26:00.125 [] [http-nio-11005-exec-1] WARN o.g.j.s.ResourceModelConfigurator - Component of class interface cannot be instantiated and will be ignored.
Kerberos 用户名 [10001874]:
10001874的 Kerberos 口令:
2023-06-26 17:26:12.619 [] [http-nio-11005-exec-1] INFO c.s.s.b.bank.util.impl.SftpUtil - 检测目录[/]
Kerberos 用户名 [10001874]: 10001874的 Kerberos 口令:
2023-06-26 17:26:22.724 [] [http-nio-11005-exec-1] INFO c.s.s.b.bank.util.impl.SftpUtil - 检测目录[/202306]
Kerberos 用户名 [10001874]: 10001874的 Kerberos 口令:
2023-06-26 17:26:37.764 [] [http-nio-11005-exec-1] INFO c.s.s.b.bank.util.impl.SftpUtil - 检测目录[/batchPhoneDestroyUser/202306]
2023-06-26 17:26:37.770 [] [http-nio-11005-exec-1] INFO c.s.s.b.bank.util.impl.SftpUtil - Start to upload E:/202306/LN00_20230626_00010.txt to /202306
2023-06-26 17:26:37.773 [] [http-nio-11005-exec-1] INFO c.s.s.b.bank.util.impl.SftpUtil - Start to upload E:/202306/LN00_20230626_00010.txt to /202306 success
2023-06-26 17:26:50.221 [] [http-nio-11005-exec-2] INFO c.s.s.b.bank.util.impl.SftpUtil - 检测目录[/batchPhoneDestroyUser]
2023-06-26 17:26:50.225 [] [http-nio-11005-exec-2] INFO c.s.s.b.bank.util.impl.SftpUtil - 检测目录[/batchPhoneDestroyUser/202306]
2023-06-26 17:26:50.228 [] [http-nio-11005-exec-2] INFO c.s.s.b.bank.util.impl.SftpUtil - 检测目录[/batchPhoneDestroyUser/202306]
2023-06-26 17:26:50.233 [] [http-nio-11005-exec-2] INFO c.s.s.b.bank.util.impl.SftpUtil - Start to upload E:/202306/LN00_20230626_00010.txt to /202306
2023-06-26 17:26:50.239 [] [http-nio-11005-exec-2] INFO c.s.s.b.bank.util.impl.SftpUtil - Start to upload E:/202306/LN00_20230626_00010.txt to /202306 success
原因分析:
当提示输入Kerberos用户名和口令时,如果不输入用户名和口令,那么等一会就连接超时, 如果手工输入回车键,程序也能正常跑通。 从问题现象上看,使用JSch连接sftp时,自动做了kerberos认证。
Kerberos认证协议:
Kerberos是一种网络认证协议,其设计目标是通过密钥系统为客户机 / 服务器应用程序提供强大的认证服务。使用Kerberos时,一个客户端需要经过三个步骤来获取服务:
-
认证:客户端向认证服务器发送一条报文,并获取一个含时间戳的Ticket-Granting Ticket(TGT)。
-
授权:客户端使用TGT向Ticket-Granting Server(TGS)请求一个服务Ticket。
-
服务请求:客户端向服务器出示服务Ticket,以证实自己的合法性。该服务器提供客户端所需服务,在Hadoop应用中,服务器可以是namenode或jobtracker。
从com.jcraft.jsch.Session 的connect方法我们可以了解到Session通过Jsch读取PreferredAuthentications的值, 然后将值通过逗号分割,然后处理认证
package com.jcraft.jsch;
public class Session implements Runnable{
public String getConfig(String key){
Object foo=null;
if(config!=null){
foo=config.get(key);
if(foo instanceof String) return (String)foo;
}
//Session获取jsch 的config属性
foo=jsch.getConfig(key);
if(foo instanceof String) return (String)foo;
return null;
}
public void connect(int connectTimeout) throws JSchException{
//略
//获取PreferredAuthentications配置属性
//实际上这里是获取jsch 成员config的信息
String cmethods=getConfig("PreferredAuthentications");
//通过逗号分割字符串
String[] cmethoda=Util.split(cmethods, ",");
String smethods=null;
if(!auth){
smethods=((UserAuthNone)ua).getMethods();
if(smethods!=null){
smethods=smethods.toLowerCase();
}
else{
// methods: publickey,password,keyboard-interactive
//smethods="publickey,password,keyboard-interactive";
smethods=cmethods;
}
}
String[] smethoda=Util.split(smethods, ",");
int methodi=0;
//循环认证处理
loop:
while(true){
while(!auth && cmethoda!=null && methodi<cmethoda.length){
}
}
}
}
在查看com.jcraft.jsch.JSch中的getConfig方法实现会发现, PreferredAuthentications信息是存储在内部的一个HashTable表中默认值是"gssapi-with-mic,publickey,keyboard-interactive,password",之前了解到属性值会被逗号分割,然后处理。
package com.jcraft.jsch;
public class JSch{
static java.util.Hashtable config=new java.util.Hashtable();
static{
//...
config.put("PreferredAuthentications", "gssapi-with-mic,publickey,keyboard-interactive,password");
//...
}
public static String getConfig(String key){
synchronized(config){
return (String)(config.get(key));
}
}
}
-
publickey:基于公共密钥的安全验证方式(public key authentication method),通过生成一组密钥(public key/private key)来实现用户的登录验证。
-
keyboard-interactive:基于键盘交互的验证方式(keyboard interactive authentication method),通过服务器向客户端发送提示信息,然后由客户端根据相应的信息通过手工输入的方式发还给服务器端。
-
password:基于口令的验证方式(password authentication method),通过输入用户名和密码的方式进行远程机器的登录验证。
-
gssapi-with-mic: (Generic Security Services Application Programming Interface with Message Integrity Check)是一种客户端/服务端应用程序编程接口,它提供了一种标准的身份验证机制和消息完整整保护机制, 它使用Kerberos票据进行身份验证,并使用数字签名的消息完整性进行保护,GSSAPI通常与Kerberos秘钥分分系统一起使用,这意味着用户只需使用一个Kerberos身份验证标识符就可以访问多个服务器
可见本问题是PreferredAuthentications默认配置了gssapi-with-mic引起的。
gssapi-with-mic的身份验证流程
1.客户端通过用户名和口令在Kerberos服务器上获取票证
2.客户端将该票证联通自己的身份验证请求一起发送到服务器
3.服务器使用Kerberos秘钥分发系统来检查接收和拒绝请求
gssapi的数据完整性保护流程
1.客户端向服务器端发送数据
2.客户端使用加密算法对数据加密
3.客户端将哈希值(HMAC)追加到加密数据的末尾
4.服务器使用哈斯算法计算收到的数据和哈希值,然后将其和客户端发送的哈希值进行对比
解决方案:
使用JSch实现sftp连接时设置PreferredAuthentications属性,取消gssapi-with-mic支持后就不会再要求验证Kerberos 口令了
参考代码如下:
@Override
public ChannelSftp create() throws Exception {
JSch jSch = new JSch();
Session session = jSch.getSession(username, host, port);
session.setPassword(password);
session.setConfig("StrictHostKeyChecking", "no");
//设置PreferredAuthentications 不使用gssapi-with-mic
session.setConfig(
"PreferredAuthentications",
"publickey,keyboard-interactive,password");
session.connect();
ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp");
channelSftp.connect();
return channelSftp;
}
参考资料
- java - Skipping Kerberos authentication prompts with JSch - Stack Overflow
- publickey,gssapi-with-mic意思 - 百度文库
上一篇:如何使用SonarQube+ SonarScanner分析项目