JSch 异常:没有注册名为 com.sun.security.jgss.krb5.initiate 的配置

JSch exception: No Configuration was registered named com.sun.security.jgss.krb5.initiate

我是运行这段代码,但是得到一个异常

Session session = null;  
Channel channel = null;  

JSch jsch = new JSch();  
session = jsch.getSession(ftpUserName, ftpHost, ftpPort);  
if (session == null) {  
    throw new Exception("session is null");  
}  
session.setPassword(ftpPassword);  
session.setTimeout(100000);  
session.setConfig("StrictHostKeyChecking", "no");  
session.connect();  

此代码在本地开发环境中运行正常。但是在测试环境部署的时候报异常。测试环境为linux环境,部署中间件为weblogic11,使用jdk均为oracle 1.6.45.

Exception:
java.lang.IllegalArgumentException: No Configuration was registered that can handle the configuration named com.sun.security.jgss.krb5.initiate
at com.bea.common.security.jdkutils.JAASConfiguration.getAppConfigurationEntry(JAASConfiguration.java:130)
at sun.security.jgss.LoginConfigImpl.getAppConfigurationEntry(LoginConfigImpl.java:139)
at javax.security.auth.login.LoginContext.init(LoginContext.java:243)
at javax.security.auth.login.LoginContext.<init>(LoginContext.java:499)
at sun.security.jgss.GSSUtil.login(GSSUtil.java:244)
at sun.security.jgss.krb5.Krb5Util.getTicket(Krb5Util.java:136)
at sun.security.jgss.krb5.Krb5InitCredential.run(Krb5InitCredential.java:328)
at java.security.AccessController.doPrivileged(Native Method)
at sun.security.jgss.krb5.Krb5InitCredential.getTgt(Krb5InitCredential.java:325)
at sun.security.jgss.krb5.Krb5InitCredential.getInstance(Krb5InitCredential.java:128)
at sun.security.jgss.krb5.Krb5MechFactory.getCredentialElement(Krb5MechFactory.java:106)
at sun.security.jgss.krb5.Krb5MechFactory.getMechanismContext(Krb5MechFactory.java:172)
at sun.security.jgss.GSSManagerImpl.getMechanismContext(GSSManagerImpl.java:209)
at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:195)
at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:162)
at com.jcraft.jsch.jgss.GSSContextKrb5.init(GSSContextKrb5.java:129)
at com.jcraft.jsch.UserAuthGSSAPIWithMIC.start(UserAuthGSSAPIWithMIC.java:135)
at com.jcraft.jsch.Session.connect(Session.java:470)
at com.jcraft.jsch.Session.connect(Session.java:183)
at com.sinosoft.FXQ.util.SftpUtils.downloadSftpFile(SftpUtils.java:57)
at com.sinosoft.FXQ.blackList.action.BLBlackListAction.extractBlackList(BLBlackListAction.java:47)
at com.sinosoft.FXQ.task.DownBlackListDataTask.run(DownBlackListDataTask.java:93)
at java.util.TimerThread.mainLoop(Timer.java:512)
at java.util.TimerThread.run(Timer.java:462)

说明

您收到的错误消息与网络身份验证协议 Kerberos 有关。我看到在您的会话配置中您没有明确指定您的身份验证协议。因此,我相信无论出于何种原因,JSch 都默认使用 Kerberos,即使您提供了用户名和密码也是如此。

修复

我认为解决此问题的最简单方法是修改您的 setConfig 方法以包含多种身份验证协议。启用所有这些并指定您的用户名和密码后,您可能会在大多数环境中进行身份验证。

   Hashtable<String, String> properties = new Hashtable<String, String>();
   properties.put("PreferredAuthentications", "gssapi-with-mic,publickey,keyboard-interactive,password");
   properties.put("StrictHostKeyChecking", "no");

   session.setConfig(properties);

还是不行?

如果上述修改不起作用,请尝试以下操作。

创建一个名为 SessionUserInfo.java

的新 class
public class SessionUserInfo implements UserInfo, UIKeyboardInteractive {

    private String password;
    private String passPhrase;
    public SessionUserInfo() {
        password = "";
        passPhrase = "";
    }

    public SessionUserInfo (String password, String passPhrase) {
        this.password = password;
        this.passPhrase = passPhrase;
    }

    public String getPassphrase() {
        return passPhrase;
    }

    public String getPassword() {
        return password;
    }

    public boolean promptPassphrase(String phrase) {
        return true;
    }

    public boolean promptPassword(String password) {
        return false;
    }

    public boolean promptYesNo(String bool) {
        return true;
    }

    public void showMessage(String message) {
        // Do Nothing
    }
}

然后修改你的代码如下。

 public static void downloadSftpFile(String ftpHost, String ftpUserName,  
        String ftpPassword, int ftpPort, String ftpPath, String localPath,  
        String fileName) throws Exception { 
    Session session = null;  
    Channel channel = null;  

    JSch jsch = new JSch();  
    session = jsch.getSession(ftpUserName, ftpHost, ftpPort);  
    if (session == null) {  
        throw new Exception("session is null");  
    }  
    session.setPassword(ftpPassword);  
    session.setTimeout(100000);  

   Hashtable<String, String> properties = new Hashtable<String, String>();
   properties.put("PreferredAuthentications", "gssapi-with-mic,publickey,keyboard-interactive,password");
   properties.put("StrictHostKeyChecking", "no");

   session.setConfig(properties);
   session.setUserInfo(new SessionUserInfo(ftpPassword, null));
    session.connect();  

    channel = session.openChannel("sftp");  
    channel.connect();  
    ChannelSftp chSftp = (ChannelSftp) channel;  

    String ftpFilePath = ftpPath + "/" + fileName;  
    String localFilePath = localPath + File.separatorChar + fileName;  

    try {  
        chSftp.get(ftpFilePath, localPath);  
    } catch (Exception e) {  
        e.printStackTrace(); 
        logger.info("download error.");  
        throw e;
    } finally {  
        chSftp.quit();  
        channel.disconnect();  
        session.disconnect();  
    }  
 }  

不通过 JSch 代码(在 GitHub BTW 上可用),我相信在连接之前向会话添加用户信息 POJO 与向会话本身添加密码具有不同的效果

日志记录 - 事后思考

我上面的发帖人发现了我发布的代码的问题并解决了原始发帖人的问题。我查看了 JSch 库中的 Session 代码,发现为 JSch 启用日志记录会对您有很大帮助。这是启用日志记录的示例。

MyLoggerclass

Code from Jsch Example

  public static class MyLogger implements com.jcraft.jsch.Logger {
    static java.util.Hashtable name=new java.util.Hashtable();
    static{
      name.put(new Integer(DEBUG), "DEBUG: ");
      name.put(new Integer(INFO), "INFO: ");
      name.put(new Integer(WARN), "WARN: ");
      name.put(new Integer(ERROR), "ERROR: ");
      name.put(new Integer(FATAL), "FATAL: ");
    }
    public boolean isEnabled(int level){
      return true;
    }
    public void log(int level, String message){
      System.err.print(name.get(new Integer(level)));
      System.err.println(message);
    }
  }

要启用它,只需修改您的代码

Session session = null;  
Channel channel = null;  
JSch.setLogger(new MyLogger());

JSch jsch = new JSch();  
session = jsch.getSession(ftpUserName, ftpHost, ftpPort);  
if (session == null) {  
    throw new Exception("session is null");  
}  
session.setPassword(ftpPassword);  
session.setTimeout(100000);  
session.setConfig("StrictHostKeyChecking", "no");  
session.connect();  

正如@Chris 已经回答的那样,JSch 默认尝试Kerberos/GSSAPI 身份验证。

由于您似乎不希望这样做,因此从默认的 JSch 身份验证方法列表 (gssapi-with-mic,publickey,keyboard-interactive,password) 中删除 Kerberos/GSSAPI (gssapi-with-mic):

session.setConfig("PreferredAuthentications", "publickey,keyboard-interactive,password");