zoukankan      html  css  js  c++  java
  • jsch配置sftp服务器ssh免密登录

    前期对接了一个通过ssh免密登录的需求,是基于原先密码登录sftp服务器的代码上进行改造,实际上代码改动量非常少,趁此机会对自己整理的资料做一下总结。

    1. 什么是SFTP

    SFTP是一个安全文件传送协议,可以为传输文件提供一种安全的加密方法。SFTP 为 SSH的一部份,是一种传输文件到服务器的安全方式。SFTP是使用加密传输认证信息和传输的数据,所以,使用SFTP是非常安全的。但是,由于这种传输方式使用了加密/解密技术,所以传输效率比普通的FTP要低得多,如果您对网络安全性要求更高时,可以使用SFTP代替FTP。

    2. 什么是Jsch以及它的作用

    Jsch是一个纯粹的用java实现SSH功能的java library。如果要知道Jsch的功能需先了解一下SSH。SSH是一个安全协议,用来在不同系统或者服务器之间进行安全连接,在连接和传送数据的过程中会进行加密。SSH一般是基于客户端的或者Linux命令行,比如window同过OpenSSH、putty等客户端的工具,在linux上可以通过ssh username@host命令进行连接。但是如果在Java中如何实现SSH呢?这时候便是通过JSCH来实现此的功能。

    3.  sftp服务器认证机制

    Jsch提供了四种认证机制:

    • password 密码方式
    • publickey(DSA,RSA) 公私钥方式
    • keyboard-interactive
    • gss-api-with-mic

     其中publickey方式通过配置公私钥实现SSH免密登录,这里也只是简单讲一下它的使用,深入使用和需要自己研究。

    4. publickey和password两种方式登录sftp的API调用

    SSH公钥检查机制:

    公钥检查机制是一个安全机制,可以防范中间人劫持等黑客攻击。SSH连接远程主机时,会检查主机的公钥。如果是第一次该主机,会显示该主机的公钥摘要,提示用户是否信任该主机。当选择接受,就会将该主机的公钥追加到文件 ~/.ssh/known_hosts 中。当再次连接该主机时,就不会再提示该问题了。 但是在某些特殊的情况下,严格的SSH公钥检查可能会破坏一些依赖SSH协议的自动化任务如Java的Jsch免密登录sftp程序。解决方式为调整StrictHostKeyChecking配置指令。StrictHostKeyChecking选项如下3种:

    • no 最不安全的级别,当然也没有那么多烦人的提示了,相对安全的内网测试时建议使用。如果连接server的key在本地不存在,那么就自动添加到文件中(默认是known_hosts),并且给出一个警告。
    • ask 默认的级别,就是出现刚才的提示了。如果连接和key不匹配,给出提示,并拒绝登录。
    • yes 最安全的级别,如果连接与key不匹配,就拒绝连接,不会提示详细信息。

    下面根据password来分析publickey方式与其区别:

    • 原来password方式需要这样一段代码来设置密码:session.setPassword (properties.getPassword ());  ,但是ssh key的方式就没有password了,所以这段要删掉。
    • publickey需要设置我们的ssh私钥文件的全路径(privateKeyFile):jsch.addIdentity (properties.getPrivateKeyFile ());
    • 一般私钥文件需要口令(passphrase)才能读取,这需要设置一个配置类对象,在jsch里其实需要自己搞一个简单的接口实现(如下:SftpAuthKeyUserInfo类 ),然后增加:session.setUserInfo(new SftpAuthKeyUserInfo (properties.getPassphrase ()));

    因此代码可以如下改造:

    1. 设置配置类对象

    SftpAuthKeyUserInfo.java

    import com.jcraft.jsch.UserInfo;
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * ssh private key passphrase info
     */
    @Slf4j
    public class SftpAuthKeyUserInfo implements UserInfo {
        /**
         * ssh private key passphrase
         */
        private String passphrase;
    
        public SftpAuthKeyUserInfo (String passphrase) {
            this.passphrase = passphrase;
        }
    
        @Override
        public String getPassphrase() {
            return passphrase;
        }
    
        @Override
        public String getPassword() {
            return null;
        }
    
        @Override
        public boolean promptPassphrase(String s) {
            return true;
        }
    
        @Override
        public boolean promptPassword(String s) {
            return false;
        }
    
        @Override
        public boolean promptYesNo(String s) {
            return true;
        }
        
        @Override
        public void showMessage(String message) {
            log.info ("SSH Message:{}", message);
        }
    }

    2. 改造以适配publickey登录方式

        JSch jsch = new JSch();
        if (StringUtils.isNotBlank(properties.getPrivateKeyFile())) {
            jsch.addIdentity(properties.getPrivateKeyFile());
        }
        // 根据用户名,主机ip,端口获取一个Session对象
        Session session = jsch.getSession(properties.getUsername(), properties.getHost(), properties.getPort());
        switch (properties.getAuthType()) {
            case PASSWORD:
                Objects.requireNonNull(properties.getPassword());
                session.setPassword(properties.getPassword());
                break;
            case PUBLIC_KEY:
                Objects.requireNonNull(properties.getPrivateKeyFile());
                if (StringUtils.isBlank(properties.getPassphrase())) {
                    throw new IllegalArgumentException("口令不为能空(私钥未加密时填任意值)");
                }
                session.setUserInfo(new SftpAuthKeyUserInfo(properties.getPassphrase()));
                break;
        }
        // 设置timeout时间
        session.setTimeout(properties.getConnectTimeout());
        // 设置keep-alive消息发送间隔(milliseconds)
        session.setServerAliveCountMax(properties.getServerAliveCountMax());
        // 设置发送keep-alive消息的最大次数
        session.setServerAliveInterval(properties.getServerAliveInterval());
        //第一次登陆时候,是否需要提示信息
        session.setConfig("StrictHostKeyChecking", "no");
        //设置ssh的DH秘钥交换
        session.setConfig("kex", "diffie-hellman-group1-sha1");
        //跳过Kerberos username 身份验证提示
        session.setConfig("PreferredAuthentications", "publickey,keyboard-interactive,password");
        // 通过Session建立链接
        synchronized (properties) {
            session.connect();
        }
        // 打开SFTP通道
        ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
        // 建立SFTP通道的连接
        channel.connect();
        if (log.isDebugEnabled()) {
            log.debug("SSH Channel connected.session={},channel={}", session, channel);
        }

    参考: https://www.jb51.net/article/172545.htm

  • 相关阅读:
    net.sf.ezmorph.bean.MorphDynaBean cannot be cast to java.util.Map
    oracle 导入excel
    【长文干货】浅析分布式系统
    35 个 Java 代码性能优化总结
    阿里巴巴Java开发手册-命名规约
    阿里巴巴Java开发手册-集合处理
    阿里巴巴Java开发手册-并发处理
    [NOI2016]国王饮水记
    python学习(二十一) Python 中的链式赋值
    python学习(二十) Python 中的比较:is 与 ==
  • 原文地址:https://www.cnblogs.com/zjfjava/p/11735837.html
Copyright © 2011-2022 走看看