添加ssh 服务 2023年3月11日17:59:54

master
zhangmeng 2023-03-13 18:10:14 +08:00
parent e3065d4ffb
commit e095236234
5 changed files with 394 additions and 187 deletions

View File

@ -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<String> uuid = new SimpleObjectProperty<>("2023311");
private SimpleObjectProperty<String> uuid = new SimpleObjectProperty<>("202331112221255544555");
private SimpleObjectProperty<Integer> status = new SimpleObjectProperty<>(0);
public static ObservableList<String> message_list = FXCollections.observableArrayList();
private WebSocketServer webSocketServer;
private WebSocketClient webSocketClient;
private SSHService sshService;
private SimpleObjectProperty<String> 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<String> message_list = SSHServiceImpl.message_list;
message_list.addListener((ListChangeListener<String>) 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);
});

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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<String, Object> sshMap = new ConcurrentHashMap<>();
//线程池
private final ExecutorService executorService = Executors.newCachedThreadPool();
public static ObservableList<String> 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;
}
}
}