🔨 优化 sftp 代码逻辑.

This commit is contained in:
lijiahang
2024-02-19 17:32:20 +08:00
parent 104090887e
commit 4117fb3ffa
15 changed files with 163 additions and 54 deletions

View File

@@ -77,4 +77,6 @@ public interface ErrorMessage {
String CONNECT_ERROR = "连接失败"; String CONNECT_ERROR = "连接失败";
String PATH_NOT_NORMALIZE = "路径不合法";
} }

View File

@@ -1,6 +1,7 @@
package com.orion.ops.framework.common.utils; package com.orion.ops.framework.common.utils;
import com.orion.lang.utils.Arrays1; import com.orion.lang.utils.Arrays1;
import com.orion.lang.utils.io.Files1;
import com.orion.ops.framework.common.constant.ErrorMessage; import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.spring.SpringHolder; import com.orion.spring.SpringHolder;
@@ -116,4 +117,15 @@ public class Valid extends com.orion.lang.utils.Valid {
return effect; return effect;
} }
/**
* 检查路径是否合法化 即不包含 ./ ../
*
* @param path path
*/
public static String checkNormalize(String path) {
Valid.notBlank(path);
Valid.isTrue(Files1.isNormalize(path), ErrorMessage.PATH_NOT_NORMALIZE);
return Files1.getPath(path);
}
} }

View File

@@ -2,9 +2,12 @@ package com.orion.ops.module.asset.handler.host.terminal.handler;
import com.orion.ops.framework.websocket.core.utils.WebSockets; import com.orion.ops.framework.websocket.core.utils.WebSockets;
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum; import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload; import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.WebSocketSession;
import javax.annotation.Resource;
/** /**
* 终端消息处理器 基类 * 终端消息处理器 基类
* *
@@ -14,6 +17,9 @@ import org.springframework.web.socket.WebSocketSession;
*/ */
public abstract class AbstractTerminalHandler<T extends TerminalBasePayload> implements ITerminalHandler<T> { public abstract class AbstractTerminalHandler<T extends TerminalBasePayload> implements ITerminalHandler<T> {
@Resource
protected TerminalManager terminalManager;
/** /**
* 发送消息 * 发送消息
* *

View File

@@ -1,15 +1,11 @@
package com.orion.ops.module.asset.handler.host.terminal.handler; package com.orion.ops.module.asset.handler.host.terminal.handler;
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
import com.orion.ops.module.asset.handler.host.terminal.model.request.SshInputRequest; import com.orion.ops.module.asset.handler.host.terminal.model.request.SshInputRequest;
import com.orion.ops.module.asset.handler.host.terminal.session.ISshSession; import com.orion.ops.module.asset.handler.host.terminal.session.ISshSession;
import com.orion.ops.module.asset.handler.host.terminal.session.ITerminalSession;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.WebSocketSession;
import javax.annotation.Resource;
/** /**
* ssh 处理输入处理器 * ssh 处理输入处理器
* *
@@ -21,17 +17,12 @@ import javax.annotation.Resource;
@Component @Component
public class SshInputHandler extends AbstractTerminalHandler<SshInputRequest> { public class SshInputHandler extends AbstractTerminalHandler<SshInputRequest> {
@Resource
private TerminalManager terminalManager;
@Override @Override
public void handle(WebSocketSession channel, SshInputRequest payload) { public void handle(WebSocketSession channel, SshInputRequest payload) {
// 获取会话 // 获取会话
ITerminalSession session = terminalManager.getSession(channel.getId(), payload.getSessionId()); ISshSession session = terminalManager.getSession(channel.getId(), payload.getSessionId());
if (session instanceof ISshSession) { // 处理输入
// 处理输入 session.write(payload.getCommand());
((ISshSession) session).write(payload.getCommand());
}
} }
} }

View File

@@ -1,15 +1,11 @@
package com.orion.ops.module.asset.handler.host.terminal.handler; package com.orion.ops.module.asset.handler.host.terminal.handler;
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
import com.orion.ops.module.asset.handler.host.terminal.model.request.SshResizeRequest; import com.orion.ops.module.asset.handler.host.terminal.model.request.SshResizeRequest;
import com.orion.ops.module.asset.handler.host.terminal.session.ISshSession; import com.orion.ops.module.asset.handler.host.terminal.session.ISshSession;
import com.orion.ops.module.asset.handler.host.terminal.session.ITerminalSession;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.WebSocketSession;
import javax.annotation.Resource;
/** /**
* ssh 修改大小处理器 * ssh 修改大小处理器
* *
@@ -21,17 +17,12 @@ import javax.annotation.Resource;
@Component @Component
public class SshResizeHandler extends AbstractTerminalHandler<SshResizeRequest> { public class SshResizeHandler extends AbstractTerminalHandler<SshResizeRequest> {
@Resource
private TerminalManager terminalManager;
@Override @Override
public void handle(WebSocketSession channel, SshResizeRequest payload) { public void handle(WebSocketSession channel, SshResizeRequest payload) {
// 获取会话 // 获取会话
ITerminalSession session = terminalManager.getSession(channel.getId(), payload.getSessionId()); ISshSession session = terminalManager.getSession(channel.getId(), payload.getSessionId());
if (session instanceof ISshSession) { // 修改大小
// 修改大小 session.resize(payload.getCols(), payload.getRows());
((ISshSession) session).resize(payload.getCols(), payload.getRows());
}
} }
} }

View File

@@ -17,7 +17,6 @@ import com.orion.ops.module.asset.entity.request.host.HostConnectLogCreateReques
import com.orion.ops.module.asset.enums.HostConnectStatusEnum; import com.orion.ops.module.asset.enums.HostConnectStatusEnum;
import com.orion.ops.module.asset.enums.HostConnectTypeEnum; import com.orion.ops.module.asset.enums.HostConnectTypeEnum;
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum; import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalCheckRequest; import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalCheckRequest;
import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalCheckResponse; import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalCheckResponse;
import com.orion.ops.module.asset.handler.host.terminal.session.ITerminalSession; import com.orion.ops.module.asset.handler.host.terminal.session.ITerminalSession;
@@ -53,9 +52,6 @@ public class TerminalCheckHandler extends AbstractTerminalHandler<TerminalCheckR
@Resource @Resource
private OperatorLogFrameworkService operatorLogFrameworkService; private OperatorLogFrameworkService operatorLogFrameworkService;
@Resource
private TerminalManager terminalManager;
@Override @Override
public void handle(WebSocketSession channel, TerminalCheckRequest payload) { public void handle(WebSocketSession channel, TerminalCheckRequest payload) {
Long hostId = payload.getHostId(); Long hostId = payload.getHostId();

View File

@@ -1,13 +1,10 @@
package com.orion.ops.module.asset.handler.host.terminal.handler; package com.orion.ops.module.asset.handler.host.terminal.handler;
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload; import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.WebSocketSession;
import javax.annotation.Resource;
/** /**
* 关闭处理器 * 关闭处理器
* *
@@ -19,9 +16,6 @@ import javax.annotation.Resource;
@Component @Component
public class TerminalCloseHandler extends AbstractTerminalHandler<TerminalBasePayload> { public class TerminalCloseHandler extends AbstractTerminalHandler<TerminalBasePayload> {
@Resource
private TerminalManager terminalManager;
@Override @Override
public void handle(WebSocketSession channel, TerminalBasePayload payload) { public void handle(WebSocketSession channel, TerminalBasePayload payload) {
log.info("TerminalCloseHandler-handle start session: {}", payload.getSessionId()); log.info("TerminalCloseHandler-handle start session: {}", payload.getSessionId());

View File

@@ -14,7 +14,6 @@ import com.orion.ops.module.asset.enums.HostConnectStatusEnum;
import com.orion.ops.module.asset.enums.HostConnectTypeEnum; import com.orion.ops.module.asset.enums.HostConnectTypeEnum;
import com.orion.ops.module.asset.handler.host.terminal.constant.TerminalMessage; import com.orion.ops.module.asset.handler.host.terminal.constant.TerminalMessage;
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum; import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalConfig; import com.orion.ops.module.asset.handler.host.terminal.model.TerminalConfig;
import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalConnectRequest; import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalConnectRequest;
import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalConnectResponse; import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalConnectResponse;
@@ -46,9 +45,6 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
@Resource @Resource
private HostConnectLogService hostConnectLogService; private HostConnectLogService hostConnectLogService;
@Resource
private TerminalManager terminalManager;
@Override @Override
public void handle(WebSocketSession channel, TerminalConnectRequest payload) { public void handle(WebSocketSession channel, TerminalConnectRequest payload) {
String sessionId = payload.getSessionId(); String sessionId = payload.getSessionId();

View File

@@ -2,14 +2,12 @@ package com.orion.ops.module.asset.handler.host.terminal.handler;
import com.orion.lang.utils.collect.Maps; import com.orion.lang.utils.collect.Maps;
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum; import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload; import com.orion.ops.module.asset.handler.host.terminal.model.TerminalBasePayload;
import com.orion.ops.module.asset.handler.host.terminal.session.ITerminalSession; import com.orion.ops.module.asset.handler.host.terminal.session.ITerminalSession;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.WebSocketSession;
import javax.annotation.Resource;
import java.util.Map; import java.util.Map;
/** /**
@@ -23,9 +21,6 @@ import java.util.Map;
@Component @Component
public class TerminalPingHandler extends AbstractTerminalHandler<TerminalBasePayload> { public class TerminalPingHandler extends AbstractTerminalHandler<TerminalBasePayload> {
@Resource
private TerminalManager terminalManager;
@Override @Override
public void handle(WebSocketSession channel, TerminalBasePayload payload) { public void handle(WebSocketSession channel, TerminalBasePayload payload) {
// 发送 pong // 发送 pong

View File

@@ -52,10 +52,12 @@ public class TerminalManager {
* *
* @param channelId channelId * @param channelId channelId
* @param sessionId sessionId * @param sessionId sessionId
* @param T T
* @return session * @return session
*/ */
public ITerminalSession getSession(String channelId, String sessionId) { @SuppressWarnings("unchecked")
return channelSessions.get(channelId, sessionId); public <T extends ITerminalSession> T getSession(String channelId, String sessionId) {
return (T) channelSessions.get(channelId, sessionId);
} }
/** /**

View File

@@ -28,7 +28,7 @@ public class SftpChangeModRequest extends TerminalBasePayload {
@Schema(description = "path") @Schema(description = "path")
private String path; private String path;
@Schema(description = "权限") @Schema(description = "10进制的8进制 权限")
private String mod; private Integer mod;
} }

View File

@@ -8,10 +8,12 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import java.util.List;
/** /**
* sftp 删除文件 实体对象 * sftp 删除文件 实体对象
* <p> * <p>
* i|eff00a1|path * i|eff00a1|paths
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0
@@ -25,7 +27,7 @@ import lombok.experimental.SuperBuilder;
@Schema(name = "SftpRemoveRequest", description = "sftp 删除文件 实体对象") @Schema(name = "SftpRemoveRequest", description = "sftp 删除文件 实体对象")
public class SftpRemoveRequest extends TerminalBasePayload { public class SftpRemoveRequest extends TerminalBasePayload {
@Schema(description = "path") @Schema(description = "paths 多个用|分割")
private String path; private List<String> paths;
} }

View File

@@ -23,12 +23,12 @@ import lombok.experimental.SuperBuilder;
@Schema(name = "SftpListResponse", description = "sftp 列表响应 实体对象") @Schema(name = "SftpListResponse", description = "sftp 列表响应 实体对象")
public class SftpListResponse extends TerminalBasePayload { public class SftpListResponse extends TerminalBasePayload {
@Schema(description = "检查结果")
private Integer result;
@Schema(description = "path") @Schema(description = "path")
private String path; private String path;
@Schema(description = "结果")
private Integer result;
@Schema(description = "body") @Schema(description = "body")
private String body; private String body;

View File

@@ -34,4 +34,64 @@ public interface ISftpSession extends ITerminalSession {
*/ */
List<SftpFileResponse> list(String path, boolean showHiddenFile); List<SftpFileResponse> list(String path, boolean showHiddenFile);
/**
* 创建文件夹
*
* @param path path
*/
void mkdir(String path);
/**
* 创建文件
*
* @param path path
*/
void touch(String path);
/**
* 移动文件
*
* @param source source
* @param target target
*/
void move(String source, String target);
/**
* 删除文件
*
* @param paths paths
*/
void remove(List<String> paths);
/**
* 截断文件
*
* @param path path
*/
void truncate(String path);
/**
* 修改权限
*
* @param path path
* @param mod mod
*/
void chmod(String path, int mod);
/**
* 获取内容
*
* @param path path
* @return content
*/
String getContent(String path);
/**
* 设置内容
*
* @param path path
* @param content content
*/
void setContent(String path, String content);
} }

View File

@@ -1,5 +1,7 @@
package com.orion.ops.module.asset.handler.host.terminal.session; package com.orion.ops.module.asset.handler.host.terminal.session;
import com.orion.lang.utils.Exceptions;
import com.orion.lang.utils.Strings;
import com.orion.lang.utils.io.FileType; import com.orion.lang.utils.io.FileType;
import com.orion.lang.utils.io.Files1; import com.orion.lang.utils.io.Files1;
import com.orion.lang.utils.io.Streams; import com.orion.lang.utils.io.Streams;
@@ -7,17 +9,19 @@ import com.orion.net.host.SessionStore;
import com.orion.net.host.sftp.SftpExecutor; import com.orion.net.host.sftp.SftpExecutor;
import com.orion.net.host.sftp.SftpFile; import com.orion.net.host.sftp.SftpFile;
import com.orion.ops.framework.common.constant.Const; import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.module.asset.handler.host.terminal.model.TerminalConfig; import com.orion.ops.module.asset.handler.host.terminal.model.TerminalConfig;
import com.orion.ops.module.asset.handler.host.terminal.model.response.SftpFileResponse; import com.orion.ops.module.asset.handler.host.terminal.model.response.SftpFileResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.WebSocketSession;
import java.io.InputStream;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* 终端 ssh 会话 * 终端 sftp 会话
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0
@@ -55,6 +59,7 @@ public class SftpSession extends TerminalSession implements ISftpSession {
@Override @Override
public List<SftpFileResponse> list(String path, boolean showHiddenFile) { public List<SftpFileResponse> list(String path, boolean showHiddenFile) {
path = Files1.getPath(path);
// 查询文件 // 查询文件
List<SftpFile> files = executor.listFilesFilter(path, List<SftpFile> files = executor.listFilesFilter(path,
s -> showHiddenFile || !s.getName().startsWith(Const.DOT), s -> showHiddenFile || !s.getName().startsWith(Const.DOT),
@@ -65,6 +70,63 @@ public class SftpSession extends TerminalSession implements ISftpSession {
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@Override
public void mkdir(String path) {
path = Valid.checkNormalize(path);
executor.makeDirectories(path);
}
@Override
public void touch(String path) {
path = Valid.checkNormalize(path);
executor.touch(path);
}
@Override
public void move(String source, String target) {
source = Valid.checkNormalize(source);
executor.move(source, target);
}
@Override
public void remove(List<String> paths) {
paths.stream()
.map(Valid::checkNormalize)
.forEach(executor::remove);
}
@Override
public void truncate(String path) {
path = Valid.checkNormalize(path);
executor.truncate(path);
}
@Override
public void chmod(String path, int mod) {
path = Valid.checkNormalize(path);
executor.changeMode(path, mod);
}
@Override
public String getContent(String path) {
path = Valid.checkNormalize(path);
try (InputStream in = executor.openInputStream(path)) {
return Streams.toString(in, config.getFileContentCharset());
} catch (Exception e) {
throw Exceptions.ioRuntime(e);
}
}
@Override
public void setContent(String path, String content) {
path = Valid.checkNormalize(path);
try {
executor.write(path, Strings.bytes(content, config.getFileContentCharset()));
} catch (Exception e) {
throw Exceptions.ioRuntime(e);
}
}
@Override @Override
public void keepAlive() { public void keepAlive() {
try { try {