From e095236234df098084ba0c8ed5d4bfd3ce7355e1 Mon Sep 17 00:00:00 2001 From: zhangmeng <1334717033@qq.com> Date: Mon, 13 Mar 2023 18:10:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0ssh=20=E6=9C=8D=E5=8A=A1=2020?= =?UTF-8?q?23=E5=B9=B43=E6=9C=8811=E6=97=A517:59:54?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SSHConnectionController.java | 247 ++++++++++++++---- .../zhangmeng/tools/ssh/SSHConnectInfo.java | 42 +++ .../java/com/zhangmeng/tools/ssh/SSHData.java | 86 ++++++ .../com/zhangmeng/tools/ssh/SSHService.java | 16 +- .../zhangmeng/tools/ssh/SSHServiceImpl.java | 190 +++++--------- 5 files changed, 394 insertions(+), 187 deletions(-) create mode 100644 src/main/java/com/zhangmeng/tools/ssh/SSHConnectInfo.java create mode 100644 src/main/java/com/zhangmeng/tools/ssh/SSHData.java diff --git a/src/main/java/com/zhangmeng/tools/controller/SSHConnectionController.java b/src/main/java/com/zhangmeng/tools/controller/SSHConnectionController.java index da40b88..7cb0904 100644 --- a/src/main/java/com/zhangmeng/tools/controller/SSHConnectionController.java +++ b/src/main/java/com/zhangmeng/tools/controller/SSHConnectionController.java @@ -1,12 +1,26 @@ package com.zhangmeng.tools.controller; -import com.zhangmeng.tools.ssh.SSHService; -import com.zhangmeng.tools.ssh.SSHServiceImpl; +import cn.hutool.core.io.BufferUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IORuntimeException; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.ssh.ChannelType; +import cn.hutool.extra.ssh.JschRuntimeException; +import cn.hutool.extra.ssh.JschUtil; +import cn.hutool.socket.aio.AioSession; +import cn.hutool.socket.aio.SimpleIoAction; +import com.alibaba.fastjson.JSON; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jcraft.jsch.*; +import com.zhangmeng.tools.ssh.*; import com.zhangmeng.tools.utils.AlertUtils; import javafx.application.Platform; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.event.EventHandler; @@ -17,6 +31,26 @@ import javafx.scene.control.TextField; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.java_websocket.WebSocket; +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.drafts.Draft_6455; +import org.java_websocket.handshake.ClientHandshake; +import org.java_websocket.handshake.ServerHandshake; +import org.java_websocket.server.WebSocketServer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Properties; + +import static cn.hutool.extra.ssh.JschUtil.*; /** * @author : 芊芊墨客 @@ -47,64 +81,26 @@ public class SSHConnectionController { @FXML private TextField port; - private SSHService sshService = null; - - private SimpleObjectProperty uuid = new SimpleObjectProperty<>("2023311"); + private SimpleObjectProperty uuid = new SimpleObjectProperty<>("202331112221255544555"); private SimpleObjectProperty status = new SimpleObjectProperty<>(0); + public static ObservableList message_list = FXCollections.observableArrayList(); + + private WebSocketServer webSocketServer; + private WebSocketClient webSocketClient; + private SSHService sshService; + + private SimpleObjectProperty cmd = new SimpleObjectProperty<>(); + + private boolean isConnection = false; @FXML public void initialize() { - sshService = new SSHServiceImpl(); + username.setText("root"); + password.setText("root"); + host.setText("192.168.52.165"); + port.setText("22"); - connection.setOnAction(event -> { - if (username.getText().length() == 0 ){ - AlertUtils.alert_warning("用户名不能为空!"); - return; - } - - if (password.getText().length() == 0 ){ - AlertUtils.alert_warning("密码不能为空!"); - return; - } - - if (host.getText().length() == 0 ){ - AlertUtils.alert_warning("服务地址不能为空!"); - return; - } - - if (port.getText().length() == 0 ){ - AlertUtils.alert_warning("服务端口不能为空!"); - return; - } - SSHServiceImpl.SSHConnectInfo sshConnectInfo = new SSHServiceImpl.SSHConnectInfo(); - sshConnectInfo.setUsername(username.getText()); - sshConnectInfo.setPassword(password.getText()); - sshConnectInfo.setHost(host.getText()); - sshConnectInfo.setPort(Integer.parseInt(port.getText())); - - //init - this.sshService.initConnection(uuid.get(),sshConnectInfo); - - //connection - this.sshService.recvHandle("", SSHServiceImpl.Type.connect,uuid.get()); - - AlertUtils.alert_msg("连额成功!"); - status.set(1); - }); - - command.setOnAction(event -> { - if (status.getValue() != 1){ - AlertUtils.alert_warning("请连接后再试!"); - return; - } - if (command.getText().length() == 0 ){ - AlertUtils.alert_warning("请输入命令后再试!"); - return; - } - this.sshService.recvHandle(command.getText(),SSHServiceImpl.Type.command, uuid.get()); - }); - ObservableList message_list = SSHServiceImpl.message_list; message_list.addListener((ListChangeListener) c -> { while (c.next()) { if (c.wasAdded()) { @@ -116,10 +112,151 @@ public class SSHConnectionController { } } }); + sshService = new SSHServiceImpl(); + //启动socket server + webSocketServer = new WebSocketServer(new InetSocketAddress(8888)) { + @Override + public void onOpen(WebSocket webSocket, ClientHandshake clientHandshake) { + add_msg("WebSocketServer:onOpen--------------------------------------------"); + sshService.initConnection(webSocket,uuid.getValue()); + } + + @Override + public void onClose(WebSocket webSocket, int i, String s, boolean b) { + add_msg("WebSocketServer:onClose--------------------------------------------"); + sshService.close(webSocket,uuid.getValue()); + } + + @Override + public void onMessage(WebSocket webSocket, String s) { + add_msg("WebSocketServer:onMessage---" + s); + sshService.recvHandle(s,webSocket,uuid.getValue()); + } + + @Override + public void onError(WebSocket webSocket, Exception e) { + add_msg("WebSocketServer:onError--------------------------------------------"); + } + + @Override + public void onStart() { + add_msg("WebSocketServer:onStart--------------------------------------------"); + } + }; + + connection.setOnAction(event -> { + if (username.getText().length() == 0) { + AlertUtils.alert_warning("用户名不能为空!"); + return; + } + + if (password.getText().length() == 0) { + AlertUtils.alert_warning("密码不能为空!"); + return; + } + + if (host.getText().length() == 0) { + AlertUtils.alert_warning("服务地址不能为空!"); + return; + } + + if (port.getText().length() == 0) { + AlertUtils.alert_warning("服务端口不能为空!"); + return; + } + webSocketServer.start(); + getConnection(); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + //发送连接信息 + SSHData ssh_data = create_ssh_data(command.getText(), SSHData.Type.connect); + webSocketClient.send(toString(ssh_data)); + isConnection = true; + status.set(1); + }); + command.textProperty().addListener((observable, oldValue, newValue) -> { + if (newValue != null){ + char[] chars = newValue.toCharArray(); + char num = 0; + for (int i = 0; i < chars.length; i++) { + if (i == chars.length -1 ){ + num = chars[i]; + } + } + SSHData ssh_data = create_ssh_data(String.valueOf(num), SSHData.Type.command); + webSocketClient.send(toString(ssh_data)); + } + + }); + command.setOnAction(event -> { + SSHData ssh_data = create_ssh_data("\r", SSHData.Type.command); + webSocketClient.send(toString(ssh_data)); + command.setText(null); + }); } - public void receive(String msg){ + private String toString(SSHData sshData){ + return JSON.toJSONString(sshData); + } + + public SSHData create_ssh_data(String command,SSHData.Type type ){ + SSHData sshData = new SSHData(); + sshData.setUsername(username.getText()); + sshData.setPassword(password.getText()); + sshData.setHost(host.getText()); + sshData.setPort(Integer.parseInt(port.getText())); + sshData.setOperate(type); + sshData.setCommand(command); + return sshData; + } + + private void getConnection(){ + if (webSocketClient == null) { + webSocketClient( "localhost", 8888); + } + } + + public void webSocketClient( String socket_address, int socket_port) { + try { + webSocketClient = new WebSocketClient(new URI( "ws://" + socket_address + ":" + socket_port), new Draft_6455()) { + //连接服务端时触发 + @Override + public void onOpen(ServerHandshake handshakedata) { + add_msg("websocket客户端和服务器连接成功!"); + } + //收到服务端消息时触发 + @Override + public void onMessage(String message) { + + } + //和服务端断开连接时触发 + @Override + public void onClose(int code, String reason, boolean remote) { + add_msg("websocket客户端退出连接"); + } + //连接异常时触发 + @Override + public void onError(Exception ex) { + add_msg("websocket客户端和服务器连接发生错误={" + ex.getMessage() + "}"); + } + }; + webSocketClient.connect(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void add_msg(String msg) { + message_list.add(msg); + message_list.add("------------------------------------------------------------------->"); + message_list.add(System.lineSeparator()); + } + + public void receive(String msg) { Platform.runLater(() -> { this.show_result.setText(msg); }); diff --git a/src/main/java/com/zhangmeng/tools/ssh/SSHConnectInfo.java b/src/main/java/com/zhangmeng/tools/ssh/SSHConnectInfo.java new file mode 100644 index 0000000..737909e --- /dev/null +++ b/src/main/java/com/zhangmeng/tools/ssh/SSHConnectInfo.java @@ -0,0 +1,42 @@ +package com.zhangmeng.tools.ssh; + +import cn.hutool.socket.aio.AioSession; +import com.jcraft.jsch.Channel; +import com.jcraft.jsch.JSch; +import org.java_websocket.WebSocket; +import org.springframework.web.socket.WebSocketSession; + +/** + * @author : 芊芊墨客 + * @version : 1.0 + * @date : 2023-03-13 12:25 + */ +public class SSHConnectInfo { + private WebSocket webSocket; + private JSch jSch; + private Channel channel; + + public WebSocket getWebSocket() { + return webSocket; + } + + public void setWebSocket(WebSocket webSocket) { + this.webSocket = webSocket; + } + + public JSch getjSch() { + return jSch; + } + + public void setjSch(JSch jSch) { + this.jSch = jSch; + } + + public Channel getChannel() { + return channel; + } + + public void setChannel(Channel channel) { + this.channel = channel; + } +} diff --git a/src/main/java/com/zhangmeng/tools/ssh/SSHData.java b/src/main/java/com/zhangmeng/tools/ssh/SSHData.java new file mode 100644 index 0000000..ea8fcf2 --- /dev/null +++ b/src/main/java/com/zhangmeng/tools/ssh/SSHData.java @@ -0,0 +1,86 @@ +package com.zhangmeng.tools.ssh; + +/** + * @author : 芊芊墨客 + * @version : 1.0 + * @date : 2023-03-13 12:26 + */ +public class SSHData { + + public enum Type{ + connect("连接"), + command("命令"), + ; + + private String desc; + + Type(String desc) { + this.desc = desc; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + } + + //操作 + private Type operate; + private String host; + //端口号默认为22 + private Integer port = 22; + private String username; + private String password; + private String command = ""; + + public Type getOperate() { + return operate; + } + + public void setOperate(Type operate) { + this.operate = operate; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + 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; + } +} diff --git a/src/main/java/com/zhangmeng/tools/ssh/SSHService.java b/src/main/java/com/zhangmeng/tools/ssh/SSHService.java index 6baf71a..53d0d19 100644 --- a/src/main/java/com/zhangmeng/tools/ssh/SSHService.java +++ b/src/main/java/com/zhangmeng/tools/ssh/SSHService.java @@ -1,16 +1,22 @@ package com.zhangmeng.tools.ssh; +import org.java_websocket.WebSocket; +import org.springframework.web.socket.WebSocketSession; + +import java.io.IOException; + /** * @author : 芊芊墨客 * @version : 1.0 - * @date : 2023-03-11 16:16 - * 接口地址: https://blog.51cto.com/zhongmayisheng/5216135 + * @date : 2023-03-13 17:05 */ public interface SSHService { - public void initConnection(String USER_UUID_KEY, SSHServiceImpl.SSHConnectInfo sshConnectInfo); + public void initConnection(WebSocket webSocket, String uuid); - public void recvHandle(String command, SSHServiceImpl.Type type, String USER_UUID_KEY); + public void recvHandle(String buffer, WebSocket webSocket,String uuid); - public void close(String USER_UUID_KEY); + public void sendMessage(WebSocket webSocket, byte[] buffer) throws IOException; + + public void close(WebSocket webSocket,String uuid); } diff --git a/src/main/java/com/zhangmeng/tools/ssh/SSHServiceImpl.java b/src/main/java/com/zhangmeng/tools/ssh/SSHServiceImpl.java index b807642..6273e88 100644 --- a/src/main/java/com/zhangmeng/tools/ssh/SSHServiceImpl.java +++ b/src/main/java/com/zhangmeng/tools/ssh/SSHServiceImpl.java @@ -1,17 +1,21 @@ package com.zhangmeng.tools.ssh; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.databind.ObjectMapper; import com.jcraft.jsch.Channel; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; import lombok.extern.slf4j.Slf4j; +import org.java_websocket.WebSocket; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.socket.TextMessage; +import org.springframework.web.socket.WebSocketSession; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Map; import java.util.Properties; @@ -22,72 +26,91 @@ import java.util.concurrent.Executors; /** * @author : 芊芊墨客 * @version : 1.0 - * @date : 2023-03-11 16:18 + * @date : 2023-03-13 17:07 */ @Slf4j -public class SSHServiceImpl implements SSHService { +public class SSHServiceImpl implements SSHService{ //存放ssh连接信息的map private static final Map sshMap = new ConcurrentHashMap<>(); + //线程池 private final ExecutorService executorService = Executors.newCachedThreadPool(); - public static ObservableList message_list = FXCollections.observableArrayList(); - @Override - public void initConnection(String USER_UUID_KEY,SSHConnectInfo sshConnectInfo) { + public void initConnection(WebSocket webSocket, String uuid) { JSch jSch = new JSch(); + SSHConnectInfo sshConnectInfo = new SSHConnectInfo(); sshConnectInfo.setjSch(jSch); + sshConnectInfo.setWebSocket(webSocket); //将这个ssh连接信息放入map中 - sshMap.put(USER_UUID_KEY, sshConnectInfo); + sshMap.put(uuid, sshConnectInfo); } + /** + * @Description: 处理客户端发送的数据 + * @Param: [buffer, session] + * @return: void + * @Author: NoCortY + * @Date: 2020/3/7 + */ @Override - public void recvHandle(String command, Type type,String USER_UUID_KEY) { + public void recvHandle(String buffer, WebSocket webSocket,String uuid) { + ObjectMapper objectMapper = new ObjectMapper(); + SSHData webSSHData = null; + try { + webSSHData = objectMapper.readValue(buffer, SSHData.class); + } catch (IOException e) { + log.error("Json转换异常"); + log.error("异常信息:{}", e.getMessage()); + return; + } - if (Type.connect.equals(type)) { - //如果是连接请求 + if (SSHData.Type.connect.equals(webSSHData.getOperate())) { //找到刚才存储的ssh连接对象 - SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(USER_UUID_KEY); + SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(uuid); //启动线程异步处理 + SSHData finalWebSSHData = webSSHData; executorService.execute(() -> { try { - //连接到终端 - connectToSSH(sshConnectInfo); + connectToSSH(sshConnectInfo, finalWebSSHData, webSocket); } catch (JSchException | IOException e) { log.error("webssh连接异常"); log.error("异常信息:{}", e.getMessage()); - close(USER_UUID_KEY); + close(webSocket,uuid); } }); - } - if (Type.command.equals(type)) { - //如果是发送命令的请求 - SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(USER_UUID_KEY); + } else if (SSHData.Type.command.equals(webSSHData.getOperate())) { + String command = webSSHData.getCommand(); + SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(uuid); if (sshConnectInfo != null) { try { - //发送命令到终端 transToSSH(sshConnectInfo.getChannel(), command); } catch (IOException e) { log.error("webssh连接异常"); log.error("异常信息:{}", e.getMessage()); - close(USER_UUID_KEY); + close(webSocket,uuid); } } } else { log.error("不支持的操作"); - close(USER_UUID_KEY); + close(webSocket,uuid); } } @Override - public void close(String USER_UUID_KEY) { - SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(USER_UUID_KEY); + public void sendMessage(WebSocket webSocket, byte[] buffer) throws IOException { + webSocket.send(StrUtil.utf8Str(buffer)); + } + + @Override + public void close(WebSocket webSocket,String uuid) { + SSHConnectInfo sshConnectInfo = (SSHConnectInfo) sshMap.get(uuid); if (sshConnectInfo != null) { //断开连接 if (sshConnectInfo.getChannel() != null) sshConnectInfo.getChannel().disconnect(); //map中移除 - sshMap.remove(USER_UUID_KEY); + sshMap.remove(uuid); } } @@ -98,16 +121,15 @@ public class SSHServiceImpl implements SSHService { * @Author: NoCortY * @Date: 2020/3/7 */ - private void connectToSSH(SSHConnectInfo sshConnectInfo) throws JSchException, IOException { + private void connectToSSH(SSHConnectInfo sshConnectInfo, SSHData webSSHData, WebSocket webSocket) throws JSchException, IOException { Session session = null; Properties config = new Properties(); config.put("StrictHostKeyChecking", "no"); //获取jsch的会话 - JSch jSch = new JSch(); - session = jSch.getSession(sshConnectInfo.username, sshConnectInfo.host, sshConnectInfo.port); + session = sshConnectInfo.getjSch().getSession(webSSHData.getUsername(), webSSHData.getHost(), webSSHData.getPort()); session.setConfig(config); //设置密码 - session.setPassword(sshConnectInfo.password); + session.setPassword(webSSHData.getPassword()); //连接 超时时间30s session.connect(30000); @@ -124,32 +146,33 @@ public class SSHServiceImpl implements SSHService { transToSSH(channel, "\r"); //读取终端返回的信息流 - try (InputStream inputStream = channel.getInputStream()) { + InputStream inputStream = channel.getInputStream(); + try { //循环读取 byte[] buffer = new byte[1024]; int i = 0; //如果没有数据来,线程会一直阻塞在这个地方等待数据。 while ((i = inputStream.read(buffer)) != -1) { - sendMessage(Arrays.copyOfRange(buffer, 0, i)); + sendMessage(webSocket, Arrays.copyOfRange(buffer, 0, i)); } + } finally { //断开连接后关闭会话 session.disconnect(); channel.disconnect(); + if (inputStream != null) { + inputStream.close(); + } } - } - public void sendMessage(byte[] buffer){ - message_list.add(buf_to_string(buffer)); - message_list.add(System.lineSeparator()); - } - - public static String buf_to_string(byte[] buffer){ - return new String(buffer, StandardCharsets.UTF_8); } /** * @Description: 将消息转发到终端 + * @Param: [channel, data] + * @return: void + * @Author: NoCortY + * @Date: 2020/3/7 */ private void transToSSH(Channel channel, String command) throws IOException { if (channel != null) { @@ -158,91 +181,4 @@ public class SSHServiceImpl implements SSHService { outputStream.flush(); } } - - public enum Type{ - connect("连接"), - command("命令"), - ; - - private String desc; - - Type(String desc) { - this.desc = desc; - } - - public String getDesc() { - return desc; - } - - public void setDesc(String desc) { - this.desc = desc; - } - } - - public static class SSHConnectInfo{ - public String host; - private JSch jSch; - private String username; - private String password; - private int port; - private Channel channel; - - public Channel getChannel() { - return channel; - } - - public String getHost() { - return host; - } - - public void setHost(String host) { - this.host = host; - } - - public void setChannel(Channel channel) { - this.channel = channel; - } - - public SSHConnectInfo() { - } - - public SSHConnectInfo(JSch jSch, String username, String password, int port) { - this.jSch = jSch; - this.username = username; - this.password = password; - this.port = port; - } - - public JSch getjSch() { - return jSch; - } - - public void setjSch(JSch jSch) { - this.jSch = jSch; - } - - 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 int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - } }