一.Apache sshd java实现远程连接服务器并执行命令的方法
先描述一下场景,本人在通信领域工作,暂时负责命令行这一块业务,公司使用apache sshd在Java应用中嵌入sshd服务,实现了通过SecureCRT等工具调用自定义的命令的功能,现想把这一套调用过程移植到web页面,通过接口实现命令的下发,网上查找了大量资料,好多资料年代比较久远,还是花了不少时间,最终实现了该功能。
思路如下:通过两个接口实现该功能,通过startClient启动客户端,实现客户端的连接,并用token作为唯一标识,把输入输出流保存在map中,map里的数据会在客户端断开时清除对应token的数据,客户端启动后,通过sendCommand下发命令,根据token从map中取出对应的流,通过流写入命令下发再从输出流里获取返回的结果,我在代码里写了一段while死循环,是为了保证结果已经完全写入到流里再获取结果,在获取结果前reset保证每次调用获取的都是当前命令下发后返回的结果,如果涉及分布式部署,可以把map中的数据放在redis中。
代码如下:
依赖:
<dependency>
<groupId>org.apache.sshd</groupId>
<artifactId>sshd-core</artifactId>
<version>0.8.0</version>
</dependency>
package com.datang.demo.controller;
import com.datang.demo.model.ConnectionInfo;
import org.apache.sshd.ClientChannel;
import org.apache.sshd.ClientSession;
import org.apache.sshd.SshClient;
import org.springframework.web.bind.annotation.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.HashMap;
import java.util.Map;
@RestController
@CrossOrigin
@RequestMapping("/sshd")
public class SSHController {
Map<String, PipedOutputStream> inputMap = new HashMap<String, PipedOutputStream>();
Map<String, ByteArrayOutputStream> outputMap = new HashMap<String, ByteArrayOutputStream>();
/**
* 启动客户端
* @param connectionInfo
*/
@PostMapping(value = "/startClient")
public void startClient(@RequestBody ConnectionInfo connectionInfo){
String host = connectionInfo.getHost();
int port = connectionInfo.getPost();
String username = connectionInfo.getUsername();
String password = connectionInfo.getPassword();
String token = connectionInfo.getToken();
SshClient client = SshClient.setUpDefaultClient();
client.start();
try {
ClientSession session = client.connect(host, port).await().getSession();
if(session.authPassword(username, password).await().isSuccess()){
ClientChannel channel = session.createShellChannel();
PipedOutputStream pipedIn = new PipedOutputStream();
channel.setIn(new PipedInputStream(pipedIn));
ByteArrayOutputStream out = new ByteArrayOutputStream();
channel.setOut(out);
ByteArrayOutputStream err = new ByteArrayOutputStream();
channel.setErr(err);
channel.open();
inputMap.put(token, pipedIn);
outputMap.put(token, out);
channel.waitFor(ClientChannel.CLOSED, 0);
inputMap.remove(token);
outputMap.remove(token);
channel.close(false);
session.close(false);
client.stop();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 找到对应客户端并下发命令
* @param connectionInfo
*/
@PostMapping(value = "/sendCommand")
public String sendCommand(@RequestBody ConnectionInfo connectionInfo) {
String command = connectionInfo.getCommand();
String token = connectionInfo.getToken();
PipedOutputStream pipedIn = inputMap.get(token);
ByteArrayOutputStream out = outputMap.get(token);
if(pipedIn != null && out != null){
try {
if(command != null){
pipedIn.write(command.getBytes());
pipedIn.flush();
}
while (true){
byte[] a = out.toByteArray();
Thread.sleep(500);
byte[] b = out.toByteArray();
if(b.length == a.length){
out.reset();
return new String(b, "GBK");
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e){
e.printStackTrace();
}
}
return "";
}
}
package com.datang.demo.model;
public class ConnectionInfo {
// 服务端ip
private String host;
// 服务端端口
private int post;
// 用户名
private String username;
// 密码
private String password;
// 下发的命令
private String command;
// 每个客户端的唯一标识
private String token;
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPost() {
return post;
}
public void setPost(int post) {
this.post = post;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
参考:
https://tangmingjie2009.iteye.com/blog/2037048
https://blog.csdn.net/jackie_xiaonan/article/details/8749035
https://blog.csdn.net/jackie_xiaonan/article/details/8907855
二.Ganymed SSH-2 java实现远程连接服务器并执行命令的方法(Windows和Linux)
依赖:
<dependency>
<groupId>ch.ethz.ganymed</groupId>
<artifactId>ganymed-ssh2</artifactId>
<version>262</version>
</dependency>