Compare commits

...

5 Commits

Author SHA1 Message Date
jshixiong 86b8e46ce2 fix 2023-07-21 15:10:05 +08:00
jshixiong d03b6dae16 fix 2023-07-21 12:19:00 +08:00
jshixiong a61ce75af9 超算文件上传下载 2023-07-20 17:58:41 +08:00
jshixiong 66e802c750 fix 2023-06-28 10:31:27 +08:00
jshixiong 9cdca3296b 超算分支 2023-06-16 09:59:50 +08:00
8 changed files with 451 additions and 5 deletions

View File

@ -143,7 +143,11 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.2</version>
</dependency>
</dependencies> </dependencies>

View File

@ -0,0 +1,48 @@
package net.educoder.bridge.common.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* shell执行结果
*
* @author 威少
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CmdResult {
/**
* 退出码
*/
private Integer exitStatus;
/**
* 实际输出
*/
private String out;
public enum ExitStatus {
/**
* 成功
*/
SUCCESS(0),
/**
* 超时
*/
TIMEOUT(124),
/**
* 默认失败
*/
FAIL(-1);
private final int code;
ExitStatus(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}
}

View File

@ -0,0 +1,24 @@
package net.educoder.bridge.webssh.controller;
import net.educoder.bridge.common.model.ApiResult;
import net.educoder.bridge.webssh.service.JchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/ssh")
public class SshController {
@Autowired
private JchService jchService;
@GetMapping("/exec")
public ApiResult<String> exec(String cmd,String username,String secret,String host,Integer port) throws Exception {
ApiResult<String> r = new ApiResult<>();
String s = jchService.sshCommand(host, port, username, secret, cmd);
r.setMsg("success");
r.setData(s);
return r;
}
}

View File

@ -0,0 +1,91 @@
package net.educoder.bridge.webssh.controller;
import lombok.extern.slf4j.Slf4j;
import net.educoder.bridge.common.model.ApiResult;
import net.educoder.bridge.common.model.CmdResult;
import net.educoder.bridge.webssh.service.WinService;
import net.educoder.bridge.webssh.utils.CmdUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* 在windows操作
*/
@Slf4j
@RestController
@RequestMapping("/win")
public class WinController {
@Autowired
private WinService winService;
@GetMapping("/exec")
public ApiResult<String> exec(String cmd){
log.info("win exec cmd:{}",cmd);
ApiResult<String> r = new ApiResult<>();
CmdResult result = CmdUtil.executeAndGetExitStatus(cmd);
r.setCode(result.getExitStatus());
r.setMsg(result.getOut());
return r;
}
@PostMapping("/remote/upload")
public ApiResult<String> uploadFile(@RequestParam("file") MultipartFile file, String tpiId, String remoteTargetDir, String username, String secret, String host, Integer port) {
log.info("win upload file to ssh--tpiId:{}",tpiId);
ApiResult<String> r = new ApiResult<>();
r.setCode(0);
r.setMsg("success");
if (null!=file){
log.info("***uploadFile filename:{}; size:{}" ,file.getOriginalFilename(),file.getSize());
}
try {
winService.uploadFileToSsh(tpiId,file,remoteTargetDir,username,secret,host,port);
} catch (Exception e) {
log.error("win upload file to ssh ERROR--tpiId:{}; e:{}",tpiId,e);
r.setCode(500);
r.setMsg("error");
}
return r;
}
@GetMapping("/remote/download")
public void downloadFile(String tpiId, String remoteSrcDir , String username, String secret, String host, Integer port, HttpServletResponse response) {
log.info("win download file by ssh--tpiId:{}",tpiId);
File zipDir = null;
try {
zipDir = winService.downloadFileDir(tpiId,remoteSrcDir,username,secret,host,port);
String fileName = zipDir.getName();
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName);
response.setContentType("application/zip");
try (FileInputStream fileInputStream = new FileInputStream(zipDir);
OutputStream outputStream = response.getOutputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
outputStream.flush();
}
}catch (Exception e){
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
log.error("win download file by ssh ERROR--tpiId:{}; e:{}",tpiId,e);
}finally {
if (zipDir!=null && zipDir.exists()){
zipDir.delete();
}
}
}
}

View File

@ -3,6 +3,7 @@ package net.educoder.bridge.webssh.service;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -10,6 +11,9 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import com.jcraft.jsch.*;
import net.educoder.bridge.webssh.utils.AESEncryptionUtil;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -18,10 +22,6 @@ import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.WebSocketSession;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
import net.educoder.bridge.webssh.model.ConnectInfo; import net.educoder.bridge.webssh.model.ConnectInfo;
import net.educoder.bridge.webssh.model.WebscoketObj; import net.educoder.bridge.webssh.model.WebscoketObj;
@ -77,6 +77,16 @@ public class JchService {
JSONObject object = JSONObject.parseObject(buffer); JSONObject object = JSONObject.parseObject(buffer);
String tp = object.getString("tp"); String tp = object.getString("tp");
if ("init".equals(tp)) { if ("init".equals(tp)) {
// 先将ssh信息解密放入原json
JSONObject jsonData =object.getJSONObject("data");
JSONObject sshinfo = JSONObject.parseObject(AESEncryptionUtil.decrypt(jsonData.getString("ssh_info")));
if (sshinfo!=null){
jsonData.put("host",sshinfo.getString("host"));
jsonData.put("port",sshinfo.getString("port"));
jsonData.put("username",sshinfo.getString("username"));
jsonData.put("secret",sshinfo.getString("secret"));
}
logger.info("** ws_jsonData **:"+jsonData);
// 初始化连接 // 初始化连接
// {"tp":"init","data":{"host":"106.75.96.108","port":"41080","username":"root","secret":"123123","myshixun_id":"1080","rows":"30"}} // {"tp":"init","data":{"host":"106.75.96.108","port":"41080","username":"root","secret":"123123","myshixun_id":"1080","rows":"30"}}
ConnectInfo connectInfo = object.getObject("data", ConnectInfo.class); ConnectInfo connectInfo = object.getObject("data", ConnectInfo.class);
@ -310,4 +320,29 @@ public class JchService {
} }
return null; return null;
} }
/**
* 连接操作机执行命令
*/
public String sshCommand(String host, int port, String username, String password, String command) throws JSchException, IOException {
JSch jsch = new JSch();
Session session = jsch.getSession(username, host, port);
//避免SSH 的公钥检查
session.setConfig("StrictHostKeyChecking", "no");
session.setPassword(password);
session.connect();
ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
InputStream in = channelExec.getInputStream();
channelExec.setCommand(command);
channelExec.setErrStream(System.err);
channelExec.connect();
String out = IOUtils.toString(in, StandardCharsets.UTF_8);
in.close();
channelExec.disconnect();
session.disconnect();
return out;
}
} }

View File

@ -0,0 +1,106 @@
package net.educoder.bridge.webssh.service;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import net.educoder.bridge.webssh.utils.CmdUtil;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
/**
* @author jshixiong
*/
@Slf4j
@Service
public class WinService {
public static final String WINDOWS_WORKSPACE = "C:\\data\\workspace\\";
/**
* 文件上传目标路径
*/
private static final String FILE_OP_DESTINATION = "userfiles";
/**
* 文件下载目录路径
*/
private static final String FILE_DL_DESTINATION = "downloadfiles";
/**
* 上传文件到远程目录
*
* @param tpiId tpiId
* @param file 上传的文件
* @param remoteTargetDir 远程文件夹
*/
public void uploadFileToSsh(String tpiId, MultipartFile file, String remoteTargetDir , String username, String secret, String host, Integer port) throws Exception {
String localFileDir = WINDOWS_WORKSPACE + tpiId + File.separator + FILE_OP_DESTINATION;
CmdUtil.executeAndGetExitStatus("mkdir " + localFileDir);
//文件windows放本地
File localFile = new File(localFileDir + File.separator + file.getOriginalFilename());
FileUtils.copyInputStreamToFile(file.getInputStream(),localFile);
JSch jsch = new JSch();
Session session = jsch.getSession(username, host, port);
session.setPassword(secret);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
//创建远程文件夹
ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
channelExec.setCommand("mkdir -p " + remoteTargetDir);
channelExec.connect();
//上传
log.info("uploadFileToSsh: {}==>{}",localFile.getAbsolutePath(),remoteTargetDir + "/" + file.getOriginalFilename());
ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp");
channelSftp.connect();
channelSftp.put(localFile.getAbsolutePath(), remoteTargetDir + "/" + file.getOriginalFilename());
channelExec.disconnect();
channelSftp.disconnect();
session.disconnect();
//删除windows存放的文件
CmdUtil.executeAndGetExitStatus("rmdir /s /q " + localFileDir);
}
/**
* 将远程文件夹打包下载到windows工作目录
*
* @param tpiId tpiId
* @param remoteSrcDir 需下载的远程文件夹
*/
public File downloadFileDir(String tpiId, String remoteSrcDir , String username, String secret, String host, Integer port) throws Exception {
String localFileDir = WINDOWS_WORKSPACE + tpiId + File.separator + FILE_DL_DESTINATION;
CmdUtil.executeAndGetExitStatus("mkdir " + localFileDir);
//从ssh远程下载所有文件到windows
JSch jsch = new JSch();
Session session = jsch.getSession(username, host, port);
session.setPassword(secret);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
//远程打包
String zipFile = remoteSrcDir + ".zip";
ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
InputStream in = channelExec.getInputStream();
channelExec.setCommand("cd " + remoteSrcDir + " && rm -rf " + zipFile + " && zip -qr " + zipFile + " .");
channelExec.connect();
String out = IOUtils.toString(in, StandardCharsets.UTF_8);
log.info("打包=={}",out);
//获取远程zip文件
String localZipFile = localFileDir + File.separator + zipFile.substring(zipFile.lastIndexOf("/")+1);
ChannelSftp channelSftp = (ChannelSftp) session.openChannel("sftp");
channelSftp.connect();
channelSftp.get(zipFile, localZipFile);
//关闭连接
in.close();
channelExec.disconnect();
channelSftp.disconnect();
session.disconnect();
//打包文件夹
return new File(localZipFile);
}
}

View File

@ -0,0 +1,62 @@
package net.educoder.bridge.webssh.utils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* AES对称加密
* @author jshixiong
*/
public class AESEncryptionUtil {
private static final String AES_ALGORITHM = "AES/CBC/PKCS5Padding";
/**
* 16位key
*/
private static final String ENCRYPTION_KEY = "MyKey@zq12345678";
/**
* 16位IV
*/
private static final String INIT_VECTOR = "MyIV@zq123456789";
/**
* AES加密
*/
public static String encrypt(String message) {
try {
IvParameterSpec iv = new IvParameterSpec(INIT_VECTOR.getBytes(StandardCharsets.UTF_8));
SecretKeySpec secretKeySpec = new SecretKeySpec((ENCRYPTION_KEY).getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);
byte[] encryptedBytes = cipher.doFinal(message.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encryptedBytes);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* AES解密
*/
public static String decrypt(String encryptedMessage) {
try {
IvParameterSpec iv = new IvParameterSpec(INIT_VECTOR.getBytes(StandardCharsets.UTF_8));
SecretKeySpec secretKeySpec = new SecretKeySpec((ENCRYPTION_KEY).getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedMessage);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes, StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,76 @@
package net.educoder.bridge.webssh.utils;
import net.educoder.bridge.common.model.CmdResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
/**
* windows命令行工具
* @author jshixiong
*/
public class CmdUtil {
private static final Logger logger = LoggerFactory.getLogger(CmdUtil.class);
/**
* 执行cmd命令并获取输出
*/
public static String execute(String command) {
return executeAndGetExitStatus(command).getOut();
}
/**
* 执行cmd命令并获得输出及退出码失败重试 共尝试retryTimes次
*/
public static CmdResult executeAndGetExitStatus(String command, int retryTimes) {
CmdResult result = new CmdResult();
for (int i = 0; i < retryTimes; i++) {
result = executeAndGetExitStatus(command);
if (result.getExitStatus() != 0) {
logger.info("执行cmd错误, 再次执行 command: {}, result: {}, times: {}", command, result, i);
} else {
break;
}
}
return result;
}
/**
* 执行命令并获得输出以及退出码
*/
public static CmdResult executeAndGetExitStatus(String command) {
CmdResult result = new CmdResult();
StringBuilder out = new StringBuilder();
Integer exitStatus = -1;
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", command);
pb.redirectErrorStream(true);
try {
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
out.append(line);
out.append(System.getProperty("line.separator"));
}
exitStatus = process.waitFor();
} catch (Exception e) {
logger.error("执行cmd出错, command:{}", command, e);
}
result.setOut(out.toString().trim());
result.setExitStatus(exitStatus);
logger.debug("execute command: {}, out: {}, status: {}", command, out, exitStatus);
return result;
}
}