java 通过ssh 连接执行shell命令,文件传输

JSch 是SSH2的纯 Java 实现 。
JSch 允许您连接到 sshd 服务器并使用端口转发、X11 转发、文件传输等,可以将其功能集成到您自己的 Java 程序中。JSch 是在BSD 风格许可下获得许可的。

JSCH 官网:http://www.jcraft.com/jsch/
在这里插入图片描述
zip文件中有很多demo.
jar为依赖jar

maven 依赖

        <dependency>
            <groupId>com.jcraft</groupId>
            <artifactId>jsch</artifactId>
            <version>0.1.55</version>
        </dependency>

测试代码

import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.io.*;
import java.util.Properties;

@Slf4j
public class SshTest {

    private String charset = "UTF-8"; // 设置编码格式
    private static String userName = "kevin"; // 用户名
    private static String passWord= "1"; // 登录密码
    private static String host = "192.168.10.101"; // 主机IP
    private static int port = 22; //默认端口
    private static JSch jsch;
    private static Session session;

    private static ChannelSftp channelSftp;

    @BeforeAll
    public static void createConn() throws JSchException {
		JSch.setLogger(new Log4JSch()); // detail log for JSch.
        jsch = new JSch();
//        jsch = new KeyGen().keyGenTest();
        // 密钥方式
        jsch.setKnownHosts("C:\\Users\\Admin\\.ssh\\known_hosts");
//        jsch.addIdentity("~/.ssh/id_rsa", "~/.ssh/id_rsa.pub", null);
//        jsch.addIdentity("C:\\Users\\Admin\\.ssh\\id_rsa", "C:\\Users\\Admin\\.ssh\\id_rsa.pub", null);
        String privKeyPath = "C:\\Users\\Admin\\.ssh\\id_rsa";
        jsch.addIdentity(privKeyPath);

        session = jsch.getSession(userName, host, port);
        // 密码方式
//        session.setPassword(passWord);
        Properties config = new Properties();
        /*
         * 在代码里需要跳过检测。否则会报错找不到主机
         * Test ignored.
         * com.jcraft.jsch.JSchException: UnknownHostKey: 192.168.10.101. RSA key fingerprint is 10:92:98:45:d2:ea:6b:8f:c1:43:e5:df:86:e5:ae:3c
         */
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config); // 为Session对象设置properties
        int timeout = 30000;
        session.setTimeout(timeout); // 设置timeout时间
        session.connect(); // 通过Session建立与远程服务器的连接回话
        log.info("connect server host: " + host);
    }

    /**
     * 关闭连接
     */
    @AfterAll
    public static void disconnect(){
        log.info("disconnect...");
        if (channelSftp != null && channelSftp.isConnected()) {
            channelSftp.disconnect();
        }
        if(session != null && session.isConnected()){
            session.disconnect();
        }
    }

    @Test
    public void downloadFile() throws JSchException, FileNotFoundException, SftpException {
        channelSftp = (ChannelSftp) session.openChannel("sftp");
        channelSftp.connect();
        log.info("start download channel file!");
        String directory = "/tmp";
        channelSftp.cd(directory);
        String saveDir = "D:\\desktop\\"+System.currentTimeMillis()+".txt";
        File file = new File(saveDir);
        String downloadFile = "Test.java";
        channelSftp.get(downloadFile, new FileOutputStream(file));
        log.info("Download Success!");
        channelSftp.disconnect();
        log.info("end execute channel sftp!");
    }

    @Test
    public void uploadFile() throws JSchException, SftpException, FileNotFoundException {
        channelSftp = (ChannelSftp) session.openChannel("sftp");
        channelSftp.connect();
        log.info("start upload channel file!");
        String directory = "/tmp";
        channelSftp.cd(directory);
        File file = new File("D:\\desktop\\Test.java");
        channelSftp.put(new FileInputStream(file), file.getName().replace(".", System.currentTimeMillis()+"."));
        log.info("Upload Success!");
        channelSftp.disconnect();
        log.info("end execute channel sftp!");
    }

    @Test
    public void execShell() throws JSchException, IOException {
        ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
        String cmd = "pwd";
        channelExec.setCommand(cmd);         //添加传入进来的shell命令
        channelExec.setErrStream(System.err);//通道连接错误信息提示
        channelExec.connect();
        log.info("start execute channel command!");

        try(BufferedReader in = new BufferedReader(new InputStreamReader(channelExec.getInputStream()))) {
            String msg;
            log.info("start read!");
            while ((msg = in.readLine()) != null) {
                log.info("命令返回信息:{}", msg);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        channelExec.disconnect();
        log.info("end execute channel command!");
    }
}

JSch 详细日志实现。 JSch.setLogger(new Log4JSch());

import com.jcraft.jsch.Logger;

import java.util.HashMap;

public class Log4JSch implements Logger {
    private static HashMap<String, String> name = new HashMap();

    static {
        name.put(String.valueOf(DEBUG), "DEBUG");
        name.put(String.valueOf(INFO), "INFO");
        name.put(String.valueOf(WARN), "WARN");
        name.put(String.valueOf(ERROR), "ERROR");
        name.put(String.valueOf(FATAL), "FATAL");
    }

    @Override
    public boolean isEnabled(int i) {
        return true;
    }

    @Override
    public void log(int level, String message) {
        System.err.println(name.get(String.valueOf(level)) + ": " + message);
    }
}

问题1
com.jcraft.jsch.JSchException: invalid privatekey: [B@17f7cd29

查看id_rsa文件,内容如下

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC...
...
...
...MAECAwQF
-----END OPENSSH PRIVATE KEY-----

解决方案:
Jsch好像不支持上面的私钥格式,要解决这个问题,我们可以使用ssh-keygen将私钥格式转换为RSAorpem模式,再次运行上面的程序。

$ ssh-keygen -p -f ~/.ssh/id_rsa -m pem

重新检查私钥内容,它应该以BEGIN RSA.

-----BEGIN RSA PRIVATE KEY-----
MIIG4wIBAAK...
...
...
...E428GBDI4
-----END RSA PRIVATE KEY-----

再次尝试连接正常。

Admin@DESKTOP-91JEC09 MINGW64 ~
$ ssh kevin@hadoop101
Last login: Wed Oct 20 14:28:41 2021 from 192.168.10.1
[kevin@hadoop101 ~]$ client_loop: send disconnect: Connection reset by peer

Admin@DESKTOP-91JEC09 MINGW64 ~
$ ssh-keygen -p -f C:\\Users\\Admin\\.ssh\\id_rsa -m pem
Key has comment 'Admin@DESKTOP-91JEC09'
Enter new passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved with the new passphrase.

Admin@DESKTOP-91JEC09 MINGW64 ~
$ ssh kevin@hadoop101
Last login: Wed Oct 20 15:43:57 2021 from 192.168.10.1

参考文档:
https://mkyong.com/java/jsch-invalid-privatekey-exception/
http://www.jcraft.com/jsch/

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java连接远程服务器并执行命令的基本原理是使用Java提供的Socket类和SSH协议。SSH协议是一种安全的远程登录协议,它可以通过加密和认证保障数据的安全性。 在Java中,可以使用JSch这个第三方库来实现SSH连接。具体步骤如下: 1. 引入JSch库 可以在Maven中央仓库中下载JSch库,或者直接从官网下载jar包。 2. 创建一个Session对象 使用JSch创建一个Session对象,需要指定远程服务器的IP地址、用户名和密码,并设置连接超时时间。 ``` String host = "remote_host"; String user = "remote_user"; String password = "remote_password"; int port = 22; // SSH默认端口为22 JSch jsch = new JSch(); Session session = jsch.getSession(user, host, port); session.setPassword(password); session.setConfig("StrictHostKeyChecking", "no"); // 不进行主机密钥验证 session.connect(timeout); ``` 3. 创建一个Channel对象 Session对象创建成功后,需要创建一个Channel对象来执行命令。在JSch中,有三种类型的Channel对象:Shell、Exec和SFTP,分别用于交互式Shell会话、执行单个命令和进行文件传输。 这里以Exec类型的Channel对象为例,执行单个命令。 ``` ChannelExec channel = (ChannelExec) session.openChannel("exec"); String command = "ls -l"; channel.setCommand(command); channel.setInputStream(null); channel.setErrStream(System.err); InputStream in = channel.getInputStream(); channel.connect(); ``` 4. 执行命令并获取结果 Channel对象创建成功后,可以通过它来执行命令,并获取命令的输出结果。 ``` byte[] tmp = new byte[1024]; while (true) { while (in.available() > 0) { int i = in.read(tmp, 0, 1024); if (i < 0) break; System.out.print(new String(tmp, 0, i)); } if (channel.isClosed()) { if (in.available() > 0) continue; System.out.println("exit-status: " + channel.getExitStatus()); break; } try { Thread.sleep(1000); } catch (Exception e) {} } ``` 5. 关闭连接 执行命令后,需要关闭Channel和Session对象。 ``` channel.disconnect(); session.disconnect(); ``` 以上就是Java连接远程服务器并执行命令的基本原理。 ### 回答2: Java连接远程服务器并执行命令的基本原理是通过使用Java提供的Socket类与服务器建立网络连接,并通过建立的连接发送指定的命令到服务器上执行。下面是实现的基本步骤: 1. 导入Java中的相关网络编程类:使用`import java.net.*`导入`java.net`包中的类,包括Socket类和InetAddress类。 2. 获取远程服务器IP地址:使用`InetAddress.getByName("服务器IP地址")`方法获取远程服务器的IP地址,其中,服务器IP地址是指待连接的远程服务器的IP地址。 3. 创建Socket对象:使用`new Socket(ip, 端口号)`方法创建一个Socket对象,其中`ip`是步骤2中获取到的远程服务器的IP地址,`端口号`是指远程服务器的监听端口号。 4. 获取输入输出流:使用`socket.getInputStream()`和`socket.getOutputStream()`方法获取到与服务器连接的输入和输出流。 5. 向服务器发送命令:使用获取到的输出流对象,使用`write`方法将要执行命令以字节流的形式发送到服务器。 6. 接收服务器返回结果:使用获取到的输入流对象,使用`read`方法读取服务器返回的结果,并将其以字符串的形式保存起来。 7. 关闭连接:使用`socket.close()`方法关闭Socket连接。 需注意的是,在执行过程中可能会涉及到异常处理,例如连接失败、输入输出流异常等情况,需要使用try-catch语句块进行捕获和处理。 通过以上步骤,我们就可以使用Java实现连接远程服务器并执行命令的基本原理。当然,在实际应用中还有更复杂的操作和场景,例如使用SSH协议远程连接服务器,使用第三方库简化操作等等,但基本原理仍然是类似的。 ### 回答3: Java连接远程服务器并执行命令的基本原理是通过使用Java Socket编程实现的。Java提供了Socket类和ServerSocket类用于网络通信。 首先,需要在Java程序中建立一个Socket对象,用于建立与远程服务器的连接。这个Socket对象需要指定远程服务器的IP地址和端口号。通过调用Socket类的构造函数,可以创建一个客户端Socket对象。 接下来,通过该Socket对象可以获取输入输出流。可以使用输出流将需要传输的命令发送到远程服务器,也可以使用输入流接收远程服务器返回的执行结果。 在客户端发送命令后,远程服务器接收到命令后会进行解析和执行。远程服务器会将执行结果返回给客户端,客户端使用输入流接收并处理这些返回结果。 在解析和执行命令的过程中,可能会有安全性的问题。因此,在实现时应该考虑安全措施,例如验证远程服务器的身份,使用加密传输等。 此外,还可以结合Java的相关类库和框架,例如SSH库或使用SSH协议进行连接,来实现更复杂的功能和更高级的安全性。 总之,Java通过建立Socket连接,并使用输入输出流进行通信,实现连接远程服务器并执行命令的基本原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值