Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
0887d3bc29
@ -26,6 +26,7 @@ import org.springframework.web.socket.WebSocketHandler;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
|
||||
import org.springframework.web.socket.server.HandshakeInterceptor;
|
||||
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -76,6 +77,18 @@ public class OpsWebSocketAutoConfiguration {
|
||||
return new WebSocketAuthorizeRequestsCustomizer(webSocketProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return websocket 缓冲区大小配置
|
||||
*/
|
||||
@Bean
|
||||
public ServletServerContainerFactoryBean servletServerContainerFactoryBean(WebSocketProperties config) {
|
||||
ServletServerContainerFactoryBean factory = new ServletServerContainerFactoryBean();
|
||||
factory.setMaxBinaryMessageBufferSize(config.getBinaryBufferSize());
|
||||
factory.setMaxTextMessageBufferSize(config.getBinaryBufferSize());
|
||||
factory.setMaxSessionIdleTimeout(config.getSessionIdleTimeout());
|
||||
return factory;
|
||||
}
|
||||
|
||||
// ==================== Sender 相关 ====================
|
||||
|
||||
@Configuration
|
||||
|
@ -30,4 +30,15 @@ public class WebSocketProperties {
|
||||
@NotNull(message = "WebSocket 的消息发送者不能为空")
|
||||
private String senderType = "local";
|
||||
|
||||
/**
|
||||
* 二进制消息缓冲区大小 byte
|
||||
*/
|
||||
private Integer binaryBufferSize;
|
||||
|
||||
/**
|
||||
* session 最大超时时间 ms
|
||||
*/
|
||||
private Long sessionIdleTimeout;
|
||||
|
||||
|
||||
}
|
||||
|
@ -8,9 +8,7 @@ import cd.casic.framework.websocket.core.util.WebSocketFrameworkUtils;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.TypeUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketHandler;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.*;
|
||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
@ -79,5 +77,16 @@ public class JsonWebSocketMessageHandler extends TextWebSocketHandler {
|
||||
log.error("[handleTextMessage][session({}) message({}) 处理异常]", session.getId(), message.getPayload());
|
||||
}
|
||||
}
|
||||
|
||||
//处理二进制消息
|
||||
@Override
|
||||
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
|
||||
try {
|
||||
WebSocketMessageListener<Object> messageListener = listeners.get("SFTP-access-message-send");
|
||||
Long tenantId = WebSocketFrameworkUtils.getTenantId(session);
|
||||
TenantUtils.execute(tenantId, () -> messageListener.onMessage(session, message));
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
log.error("[handleBinaryMessage][session({}) message({}) 处理异常]", session.getId(), message.getPayload());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -92,4 +93,10 @@ public class TestCaseInfoController {
|
||||
|
||||
return CommonResult.success(respList);
|
||||
}
|
||||
|
||||
@PostMapping("/checkTestCaseExist")
|
||||
public CommonResult<String> checkTestCaseExist(MultipartFile file){
|
||||
String s = testCaseInfoService.checkTestCaseExist(file);
|
||||
return CommonResult.success(s);
|
||||
}
|
||||
}
|
||||
|
@ -24,4 +24,6 @@ public class TestCaseInfoReq {
|
||||
|
||||
// 描述信息
|
||||
private String remark;
|
||||
|
||||
private String sign;
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
package cd.casic.ci.process.process.dataObject.pipgroup;
|
||||
|
||||
import cd.casic.framework.commons.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class PipGroup extends BaseDO {
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
private String groupName;
|
||||
/**
|
||||
|
@ -35,4 +35,6 @@ public class TestCaseInfo extends PipBaseElement {
|
||||
|
||||
// 描述信息
|
||||
private String remark;
|
||||
|
||||
private String sign;
|
||||
}
|
||||
|
@ -8,7 +8,10 @@ import cd.casic.ci.process.process.converter.GroupConverter;
|
||||
import cd.casic.ci.process.process.dao.pipeline.PipGroupDao;
|
||||
import cd.casic.ci.process.process.dataObject.pipgroup.PipGroup;
|
||||
import cd.casic.ci.process.process.service.group.GroupService;
|
||||
import cd.casic.ci.process.process.service.pipeline.PipelineService;
|
||||
import cd.casic.ci.process.util.WebFrameworkUtils;
|
||||
import cd.casic.framework.commons.exception.ServiceException;
|
||||
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -20,6 +23,8 @@ public class GroupServiceImpl extends ServiceImpl<PipGroupDao, PipGroup> impleme
|
||||
private GroupConverter converter;
|
||||
@Resource
|
||||
private PipGroupDao groupDao;
|
||||
@Resource
|
||||
private PipelineService pipelineService;
|
||||
@Override
|
||||
public void addGroup(GroupAddReq req) {
|
||||
PipGroup pipGroup = converter.reqToDto(req);
|
||||
@ -40,6 +45,9 @@ public class GroupServiceImpl extends ServiceImpl<PipGroupDao, PipGroup> impleme
|
||||
|
||||
@Override
|
||||
public void deleteGroup(String id) {
|
||||
if (pipelineService.getGroupCount(id) != 0) {
|
||||
throw new ServiceException(GlobalErrorCodeConstants.PIPELINE_ERROR.getCode(),"分组下辖流水线不为空不允许删除");
|
||||
}
|
||||
removeById(id);
|
||||
}
|
||||
}
|
||||
|
@ -37,4 +37,5 @@ public interface PipelineService extends IService<PipPipeline> {
|
||||
PipelineFindResp findPipelineById(@Valid PipelineQueryReq pipelineQueryReq);
|
||||
|
||||
TreeRunContextResp getPipelineRunState(String pipelineId);
|
||||
Long getGroupCount(String groupId);
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
|
||||
import cd.casic.framework.commons.pojo.PageResult;
|
||||
import cd.casic.framework.security.dal.user.AdminUserDO;
|
||||
import cd.casic.framework.tenant.core.service.AdminUserServiceImpl;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
@ -528,6 +529,17 @@ public class PipelineServiceImpl extends ServiceImpl<PipelineDao, PipPipeline> i
|
||||
|
||||
return new TreeRunContextResp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getGroupCount(String groupId) {
|
||||
if (StringUtils.isEmpty(groupId)) {
|
||||
return -1L;
|
||||
}
|
||||
LambdaQueryWrapper<PipPipeline> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(PipPipeline::getGroupId,groupId);
|
||||
return pipelineDao.selectCount(wrapper);
|
||||
}
|
||||
|
||||
private String between(LocalDateTime startTime,LocalDateTime endTime){
|
||||
if (startTime==null||endTime==null) {
|
||||
return "";
|
||||
|
@ -8,6 +8,7 @@ import cd.casic.ci.process.process.dataObject.testCase.TestCaseInfo;
|
||||
import cd.casic.framework.commons.pojo.PageResult;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -34,4 +35,5 @@ public interface TestCaseInfoService extends IService<TestCaseInfo> {
|
||||
|
||||
List<String> findFileTypeList();
|
||||
List<TestCaseInfoResp> findByManagerIds(List<String> idList);
|
||||
String checkTestCaseExist(MultipartFile file);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import cd.casic.ci.process.process.converter.TestCaseInfoConverter;
|
||||
import cd.casic.ci.process.process.dao.testCase.TestCaseInfoDao;
|
||||
import cd.casic.ci.process.process.dataObject.testCase.TestCaseInfo;
|
||||
import cd.casic.ci.process.process.service.testCase.TestCaseInfoService;
|
||||
import cd.casic.ci.process.util.FileUtil;
|
||||
import cd.casic.framework.commons.exception.ServiceException;
|
||||
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
|
||||
import cd.casic.framework.commons.pojo.PageResult;
|
||||
@ -23,7 +24,9 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@ -151,6 +154,27 @@ public class TestCaseInfoServiceImpl extends ServiceImpl<TestCaseInfoDao, TestCa
|
||||
return TestCaseInfoConverter.INSTANCE.toRespList(testCaseInfos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String checkTestCaseExist(MultipartFile file) {
|
||||
try{
|
||||
InputStream inputStream = file.getInputStream();
|
||||
String md5Str = FileUtil.getMD5Str(inputStream);
|
||||
if (checkMd5Exist(md5Str)) {
|
||||
return md5Str;
|
||||
}
|
||||
} catch (Exception e ){
|
||||
log.error("校验文件内容出现错误",e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
private Boolean checkMd5Exist(String sign){
|
||||
if (StringUtils.isEmpty(sign)) {
|
||||
return false;
|
||||
}
|
||||
LambdaQueryWrapper<TestCaseInfo> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(TestCaseInfo::getSign,sign);
|
||||
return testCaseInfoDao.exists(wrapper);
|
||||
}
|
||||
|
||||
private void setUserName(TestCaseInfoResp testCaseInfoResp) {
|
||||
if (!StringUtils.isEmpty(testCaseInfoResp.getCreator())){
|
||||
|
@ -0,0 +1,28 @@
|
||||
package cd.casic.ci.process.util;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class FileUtil {
|
||||
public static String getMD5Str(InputStream inputStream) throws NoSuchAlgorithmException, IOException {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] buffer = new byte[1024];
|
||||
int len = -1;
|
||||
while((len=inputStream.read(buffer))!=-1){
|
||||
md.update(buffer,0,len);
|
||||
}
|
||||
byte[] digest = md.digest();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
for (byte b : digest) {
|
||||
String format = String.format("%02x", b);
|
||||
stringBuilder.append(format);
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
}
|
@ -7,20 +7,40 @@
|
||||
<!-- 这里暂时没有任何SQL方法 -->
|
||||
|
||||
<select id="getList" resultType="cd.casic.ci.process.dto.resp.group.GroupListResp">
|
||||
SELECT * FROM
|
||||
(SELECT * FROM pip_group
|
||||
UNION ALL
|
||||
(SELECT null AS id,
|
||||
"未分组" AS group_name,
|
||||
"PROJECT" AS type ,
|
||||
"" AS creator,
|
||||
NOW() AS create_time,
|
||||
NOW() AS update_time,
|
||||
"" AS updater)
|
||||
) pg
|
||||
LEFT JOIN
|
||||
(SELECT group_id,COUNT(*) count FROM pip_pipeline GROUP BY group_id) pl
|
||||
ON ((pg.id = pl.group_id ) OR (ISNULL(pl.group_id) AND ISNULL(pl.group_id)))
|
||||
WHERE pg.`name` LIKE #{groupName} AND ((pg.creator = #{userId} AND pg.type = 'PERSON') OR (pg.type = 'PROJECT'))
|
||||
SELECT id,
|
||||
group_name,
|
||||
type,
|
||||
creator,
|
||||
create_time,
|
||||
updater,
|
||||
update_time,
|
||||
group_id,
|
||||
IFNULL(count,0) `count` FROM
|
||||
(SELECT
|
||||
id,
|
||||
group_name,
|
||||
type,
|
||||
creator,
|
||||
create_time,
|
||||
updater,
|
||||
update_time
|
||||
FROM pip_group
|
||||
UNION ALL
|
||||
(SELECT null AS id,
|
||||
"未分组" AS group_name,
|
||||
"PROJECT" AS type ,
|
||||
"" AS creator,
|
||||
NOW() AS create_time,
|
||||
"" AS updater,
|
||||
NOW() AS update_time)
|
||||
) pg
|
||||
LEFT JOIN (SELECT group_id,COUNT(*) count FROM pip_pipeline GROUP BY group_id) pl ON (pg.id = pl.group_id OR (ISNULL(pg.id) AND ISNULL(group_id)) )
|
||||
<where>
|
||||
<if test="groupName!=null and groupName!=''">
|
||||
AND pg.`group_name` LIKE CONCAT('%',#{groupName},'%')
|
||||
</if>
|
||||
AND ((pg.creator = #{userId} AND pg.type = 'PERSON') OR (pg.type = 'PROJECT'))
|
||||
</where>
|
||||
|
||||
</select>
|
||||
</mapper>
|
52
modules/module-ci-terminal/pom.xml
Normal file
52
modules/module-ci-terminal/pom.xml
Normal file
@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>cd.casic.boot</groupId>
|
||||
<artifactId>modules</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<!--======终端部分======-->
|
||||
|
||||
<packaging>jar</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
<version>${revision}</version>
|
||||
<artifactId>module-ci-terminal</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cd.casic.boot</groupId>
|
||||
<artifactId>module-ci-machine</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cd.casic.boot</groupId>
|
||||
<artifactId>module-ci-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cd.casic.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cd.casic.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cd.casic.boot</groupId>
|
||||
<artifactId>spring-boot-starter-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--=======外部依赖======-->
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.orionsec.kit</groupId>
|
||||
<artifactId>orion-net</artifactId>
|
||||
<version>2.0.1</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,74 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
/**
|
||||
* 标准数据处理策略 基类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/6/11 21:44
|
||||
*/
|
||||
public abstract class AbstractGenericsDataStrategy<M extends GenericsDataModel> implements GenericsDataStrategy<M> {
|
||||
|
||||
protected final Class<M> modelClass;
|
||||
|
||||
public AbstractGenericsDataStrategy(Class<M> modelClass) {
|
||||
this.modelClass = modelClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新填充
|
||||
*
|
||||
* @param beforeModel 修改前的配置
|
||||
* @param afterModel 修改后的配置
|
||||
*/
|
||||
protected void updateFill(M beforeModel, M afterModel) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 预校验参数
|
||||
*
|
||||
* @param model model
|
||||
*/
|
||||
protected void preValid(M model) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验参数
|
||||
*
|
||||
* @param model model
|
||||
*/
|
||||
protected void valid(M model) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doValid(M beforeModel, M afterModel) {
|
||||
// 预校验参数
|
||||
this.preValid(afterModel);
|
||||
// 更新填充
|
||||
this.updateFill(beforeModel, afterModel);
|
||||
// 校验参数
|
||||
this.valid(afterModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public M parse(String serialModel) {
|
||||
return JSON.parseObject(serialModel, modelClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toView(M model) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public M toView(String serialModel) {
|
||||
// 解析
|
||||
M parse = this.parse(serialModel);
|
||||
// 转为视图对象
|
||||
this.toView(parse);
|
||||
return parse;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
|
||||
/**
|
||||
* aes 数据加密工具类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/8 0:05
|
||||
*/
|
||||
public class AesEncryptUtils {
|
||||
|
||||
private static AesEncryptor delegate;
|
||||
|
||||
private AesEncryptUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param plain 明文
|
||||
* @return 密文
|
||||
*/
|
||||
public static byte[] encrypt(byte[] plain) {
|
||||
return delegate.encrypt(plain);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param plain 明文
|
||||
* @return 密文
|
||||
*/
|
||||
public static byte[] encrypt(String plain) {
|
||||
return delegate.encrypt(plain);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param plain 明文
|
||||
* @return 密文
|
||||
*/
|
||||
public static String encryptAsString(String plain) {
|
||||
return delegate.encryptAsString(plain);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param plain 明文
|
||||
* @return 密文
|
||||
*/
|
||||
public static String encryptAsString(byte[] plain) {
|
||||
return delegate.encryptAsString(plain);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param text 密文
|
||||
* @return 明文
|
||||
*/
|
||||
public static byte[] decrypt(byte[] text) {
|
||||
return delegate.decrypt(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param text 密文
|
||||
* @return 明文
|
||||
*/
|
||||
public static byte[] decrypt(String text) {
|
||||
return delegate.decrypt(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param text 密文
|
||||
* @return 明文
|
||||
*/
|
||||
public static String decryptAsString(String text) {
|
||||
return delegate.decryptAsString(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param text 密文
|
||||
* @return 明文
|
||||
*/
|
||||
public static String decryptAsString(byte[] text) {
|
||||
return delegate.decryptAsString(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证加密结果
|
||||
*
|
||||
* @param plain 明文
|
||||
* @param text 密文
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean verify(String plain, String text) {
|
||||
return delegate.verify(plain, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证加密结果
|
||||
*
|
||||
* @param plain 明文
|
||||
* @param text 密文
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean verify(byte[] plain, byte[] text) {
|
||||
return delegate.verify(plain, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密后 base62 编码
|
||||
*
|
||||
* @param plain 明文
|
||||
* @return 密文
|
||||
*/
|
||||
public static String encryptBase62(String plain) {
|
||||
return delegate.encryptBase62(plain);
|
||||
}
|
||||
|
||||
/**
|
||||
* base62 解码后解密
|
||||
*
|
||||
* @param text 密文
|
||||
* @return 明文
|
||||
*/
|
||||
public static String decryptBase62(String text) {
|
||||
return delegate.decryptBase62(text);
|
||||
}
|
||||
|
||||
public static void setDelegate(AesEncryptor delegate) {
|
||||
if (AesEncryptUtils.delegate != null) {
|
||||
// unmodified
|
||||
throw Exceptions.state();
|
||||
}
|
||||
AesEncryptUtils.delegate = delegate;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.codec.Base62s;
|
||||
import cn.orionsec.kit.lang.utils.crypto.symmetric.SymmetricCrypto;
|
||||
|
||||
/**
|
||||
* aes 加密器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/8 0:20
|
||||
*/
|
||||
public interface AesEncryptor extends SymmetricCrypto {
|
||||
|
||||
/**
|
||||
* 加密后 base62 编码
|
||||
*
|
||||
* @param plain 明文
|
||||
* @return 密文
|
||||
*/
|
||||
default String encryptBase62(String plain) {
|
||||
return new String(Base62s.encode(this.encrypt(plain)));
|
||||
}
|
||||
|
||||
/**
|
||||
* base62 解码后解密
|
||||
*
|
||||
* @param text 密文
|
||||
* @return 明文
|
||||
*/
|
||||
default String decryptBase62(String text) {
|
||||
return new String(this.decrypt(Base62s.decode(text)));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import cn.orionsec.kit.lang.constant.OrionConst;
|
||||
|
||||
/**
|
||||
* 项目常量
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/6/19 18:56
|
||||
*/
|
||||
public interface AppConst extends OrionConst {
|
||||
|
||||
/**
|
||||
* 同 ${orion.version} 迭代时候需要手动更改
|
||||
*/
|
||||
String VERSION = "2.3.9";
|
||||
|
||||
/**
|
||||
* 同 ${spring.application.name}
|
||||
*/
|
||||
String APP_NAME = "orion-visor";
|
||||
|
||||
String GITHUB = "https://github.com/dromara/orion-visor";
|
||||
|
||||
String GITEE = "https://gitee.com/dromara/orion-visor";
|
||||
|
||||
String ISSUES = "https://github.com/dromara/orion-visor/issues";
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* boolean 枚举
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/9/19 10:32
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum BooleanBit {
|
||||
|
||||
/**
|
||||
* 假
|
||||
*/
|
||||
FALSE(0),
|
||||
|
||||
/**
|
||||
* 真
|
||||
*/
|
||||
TRUE(1),
|
||||
|
||||
;
|
||||
|
||||
private final Integer value;
|
||||
|
||||
/**
|
||||
* 是否为布尔值
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean booleanValue() {
|
||||
return this == TRUE;
|
||||
}
|
||||
|
||||
public static BooleanBit of(boolean value) {
|
||||
return value ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
public static BooleanBit of(Integer value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
for (BooleanBit e : values()) {
|
||||
if (e.value.equals(value)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为布尔值
|
||||
*
|
||||
* @param value value
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean toBoolean(Integer value) {
|
||||
return TRUE.value.equals(value);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
/**
|
||||
* 常量 - 中文
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/11/30 21:00
|
||||
*/
|
||||
public interface CnConst {
|
||||
|
||||
String CN_USER = "用户";
|
||||
|
||||
String CN_ROLE = "角色";
|
||||
|
||||
String CN_UNKNOWN = "未知";
|
||||
|
||||
String CN_INTRANET_IP = "内网IP";
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
/**
|
||||
* 常量
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/6/23 18:49
|
||||
*/
|
||||
public interface Const extends cn.orionsec.kit.lang.constant.Const,FieldConst, CnConst{
|
||||
|
||||
Integer NOT_DELETE = 0;
|
||||
|
||||
Integer IS_DELETED = 1;
|
||||
|
||||
int BEARER_PREFIX_LEN = 7;
|
||||
|
||||
int MD5_LEN = 32;
|
||||
|
||||
Long ROOT_PARENT_ID = 0L;
|
||||
|
||||
Integer DEFAULT_SORT = 10;
|
||||
|
||||
Long NONE_ID = -1L;
|
||||
|
||||
Integer DEFAULT_VERSION = 1;
|
||||
|
||||
Long SYSTEM_USER_ID = 0L;
|
||||
|
||||
String SYSTEM_USERNAME = "system";
|
||||
|
||||
int BATCH_COUNT = 500;
|
||||
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import cn.orionsec.kit.lang.exception.ApplicationException;
|
||||
import cn.orionsec.kit.lang.exception.argument.InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/7 18:51
|
||||
*/
|
||||
public interface ErrorMessage {
|
||||
|
||||
String MISSING = "{} 不能为空";
|
||||
|
||||
String PARAM_MISSING = "参数不能为空";
|
||||
|
||||
String PARAM_ERROR = "参数错误";
|
||||
|
||||
String ID_MISSING = "id 不能为空";
|
||||
|
||||
String INVALID_PARAM = "参数验证失败";
|
||||
|
||||
String DATA_MODIFIED = "数据发生变更, 请刷新后重试";
|
||||
|
||||
String DATA_ABSENT = "数据不存在";
|
||||
|
||||
String KEY_ABSENT = "主机密钥不存在";
|
||||
|
||||
String IDENTITY_ABSENT = "主机身份不存在";
|
||||
|
||||
String CONFIG_ABSENT = "配置不存在";
|
||||
|
||||
String CONFIG_PRESENT = "配置已存在";
|
||||
|
||||
String DATA_PRESENT = "数据已存在";
|
||||
|
||||
String NAME_PRESENT = "名称已存在";
|
||||
|
||||
String CODE_PRESENT = "编码已存在";
|
||||
|
||||
String NICKNAME_PRESENT = "花名已存在";
|
||||
|
||||
String USERNAME_PRESENT = "用户名已存在";
|
||||
|
||||
String ROLE_ABSENT = "角色不存在";
|
||||
|
||||
String ROLE_CODE_ABSENT = "角色 [{}] 不存在";
|
||||
|
||||
String INVALID_PARENT_MENU = "所选父菜单不合法";
|
||||
|
||||
String PARENT_MENU_ABSENT = "父菜单不存在";
|
||||
|
||||
String USERNAME_PASSWORD_ERROR = "用户名或密码错误";
|
||||
|
||||
String MAX_LOGIN_FAILED = "登录失败次数已上限";
|
||||
|
||||
String HISTORY_ABSENT = "历史值不存在";
|
||||
|
||||
String USER_ABSENT = "用户不存在";
|
||||
|
||||
String HOST_ABSENT = "主机不存在";
|
||||
|
||||
String GROUP_ABSENT = "分组不存在";
|
||||
|
||||
String HOST_TYPE_ERROR = "主机类型错误";
|
||||
|
||||
String HOST_NOT_ENABLED = "主机未启用";
|
||||
|
||||
String CONFIG_NOT_ENABLED = "配置未启用";
|
||||
|
||||
String UNABLE_OPERATE_ADMIN_ROLE = "无法操作管理员账号";
|
||||
|
||||
String UNSUPPORTED_CHARSET = "不支持的编码 [{}]";
|
||||
|
||||
String DECRYPT_ERROR = "数据解密失败";
|
||||
|
||||
String PASSWORD_MISSING = "请输入密码";
|
||||
|
||||
String BEFORE_PASSWORD_ERROR = "原密码错误";
|
||||
|
||||
String DATA_NO_PERMISSION = "数据无权限";
|
||||
|
||||
String ANY_NO_PERMISSION = "{}无权限";
|
||||
|
||||
String SESSION_PRESENT = "会话已存在";
|
||||
|
||||
String SESSION_ABSENT = "会话不存在";
|
||||
|
||||
String PATH_NOT_NORMALIZE = "路径不合法";
|
||||
|
||||
String OPERATE_ERROR = "操作失败";
|
||||
|
||||
String UNKNOWN_TYPE = "未知类型";
|
||||
|
||||
String ERROR_TYPE = "错误的类型";
|
||||
|
||||
String FILE_ABSENT = "文件不存在";
|
||||
|
||||
String FILE_ABSENT_CLEAR = "文件不存在 (可能已被清理)";
|
||||
|
||||
String LOG_ABSENT = "日志不存在";
|
||||
|
||||
String TASK_ABSENT = "任务不存在";
|
||||
|
||||
String CONNECT_ERROR = "连接失败";
|
||||
|
||||
String AUTH_ERROR = "认证失败";
|
||||
|
||||
String FILE_UPLOAD_ERROR = "文件上传失败";
|
||||
|
||||
String SCRIPT_UPLOAD_ERROR = "脚本上传失败";
|
||||
|
||||
String EXEC_ERROR = "执行失败";
|
||||
|
||||
String ILLEGAL_STATUS = "当前状态不支持此操作";
|
||||
|
||||
String CHECK_AUTHORIZED_HOST = "请选择已授权的主机";
|
||||
|
||||
String FILE_READ_ERROR = "文件读取失败";
|
||||
|
||||
String FILE_READ_ERROR_CLEAR = "文件读取失败 (可能已被清理)";
|
||||
|
||||
String PLEASE_CHECK_HOST_SSH = "请检查主机 {} 是否存在/权限/SSH配置";
|
||||
|
||||
String CLIENT_ABORT = "手动中断";
|
||||
|
||||
String UNABLE_DOWNLOAD_FOLDER = "无法下载文件夹";
|
||||
|
||||
/**
|
||||
* 是否为业务异常
|
||||
*
|
||||
* @param ex ex
|
||||
* @return biz exception
|
||||
*/
|
||||
static boolean isBizException(Exception ex) {
|
||||
if (ex == null) {
|
||||
return false;
|
||||
}
|
||||
return ex instanceof InvalidArgumentException
|
||||
|| ex instanceof IllegalArgumentException
|
||||
|| ex instanceof ApplicationException;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
*
|
||||
* @param ex ex
|
||||
* @param defaultMsg defaultMsg
|
||||
* @return message
|
||||
*/
|
||||
static String getErrorMessage(Exception ex, String defaultMsg) {
|
||||
if (ex == null) {
|
||||
return null;
|
||||
}
|
||||
String message = ex.getMessage();
|
||||
if (message == null) {
|
||||
return defaultMsg;
|
||||
}
|
||||
// 业务异常
|
||||
if (isBizException(ex)) {
|
||||
return message;
|
||||
}
|
||||
return defaultMsg;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
/**
|
||||
* 额外字段常量
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/28 18:34
|
||||
*/
|
||||
public interface ExtraFieldConst extends FieldConst {
|
||||
|
||||
String USER_ID = "userId";
|
||||
|
||||
String TRACE_ID = "traceId";
|
||||
|
||||
String IDENTITY = "identity";
|
||||
|
||||
String GROUP_NAME = "groupName";
|
||||
|
||||
String ID_LIST = "idList";
|
||||
|
||||
String USERNAME = "username";
|
||||
|
||||
String HOME = "home";
|
||||
|
||||
String STATUS_NAME = "statusName";
|
||||
|
||||
String KEY_NAME = "keyName";
|
||||
|
||||
String POSITION_NAME = "positionName";
|
||||
|
||||
String GRANT_TYPE = "grantType";
|
||||
|
||||
String GRANT_NAME = "grantName";
|
||||
|
||||
String CHANNEL_ID = "channelId";
|
||||
|
||||
String SESSION_ID = "sessionId";
|
||||
|
||||
String CONNECT_TYPE = "connectType";
|
||||
|
||||
String HOST_ID = "hostId";
|
||||
|
||||
String HOST_NAME = "hostName";
|
||||
|
||||
String LOG_ID = "logId";
|
||||
|
||||
String DARK = "dark";
|
||||
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
/**
|
||||
* 字段常量
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/10/17 12:44
|
||||
*/
|
||||
public interface FieldConst {
|
||||
|
||||
String ID = "id";
|
||||
|
||||
String KEY = "key";
|
||||
|
||||
String CODE = "code";
|
||||
|
||||
String NAME = "name";
|
||||
|
||||
String TITLE = "title";
|
||||
|
||||
String VALUE = "value";
|
||||
|
||||
String LABEL = "label";
|
||||
|
||||
String TYPE = "type";
|
||||
|
||||
String COLOR = "color";
|
||||
|
||||
String STATUS = "status";
|
||||
|
||||
String INFO = "info";
|
||||
|
||||
String EXTRA = "extra";
|
||||
|
||||
String REL_ID = "relId";
|
||||
|
||||
String BEFORE = "before";
|
||||
|
||||
String AFTER = "after";
|
||||
|
||||
String SOURCE = "source";
|
||||
|
||||
String TARGET = "target";
|
||||
|
||||
String CHARSET = "charset";
|
||||
|
||||
String TOKEN = "token";
|
||||
|
||||
String SEQ = "seq";
|
||||
|
||||
String PATH = "path";
|
||||
|
||||
String ADDRESS = "address";
|
||||
|
||||
String MOD = "mod";
|
||||
|
||||
String COUNT = "count";
|
||||
|
||||
String DATE = "date";
|
||||
|
||||
String TIME = "time";
|
||||
|
||||
String TEXT = "text";
|
||||
|
||||
String ISSUE = "issue";
|
||||
|
||||
String EXPIRE = "expire";
|
||||
|
||||
String LOCATION = "location";
|
||||
|
||||
String USER_AGENT = "userAgent";
|
||||
|
||||
String ERROR_MESSAGE = "errorMessage";
|
||||
|
||||
String UUID = "uuid";
|
||||
|
||||
String REDIRECT = "redirect";
|
||||
|
||||
String SCHEMA = "schema";
|
||||
|
||||
String FILTER = "filter";
|
||||
|
||||
String ALL = "all";
|
||||
|
||||
String CONFIG = "config";
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 标准数据模型
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/20 22:07
|
||||
*/
|
||||
public interface GenericsDataModel {
|
||||
|
||||
/**
|
||||
* 序列化
|
||||
*
|
||||
* @return json
|
||||
*/
|
||||
default String serial() {
|
||||
return JSON.toJSONString(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 转为 map
|
||||
*
|
||||
* @return map
|
||||
*/
|
||||
default Map<String, Object> toMap() {
|
||||
return JSON.parseObject(this.serial());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
/**
|
||||
* 标准数据处理策略
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/20 22:09
|
||||
*/
|
||||
public interface GenericsDataStrategy<M extends GenericsDataModel> {
|
||||
|
||||
/**
|
||||
* 获取默认值
|
||||
*
|
||||
* @return 默认值
|
||||
*/
|
||||
M getDefault();
|
||||
|
||||
/**
|
||||
* 执行完整验证链
|
||||
* <p>
|
||||
* preValid > updateFill > valid
|
||||
*
|
||||
* @param beforeModel beforeModel
|
||||
* @param afterModel afterModel
|
||||
*/
|
||||
void doValid(M beforeModel, M afterModel);
|
||||
|
||||
/**
|
||||
* 解析数据
|
||||
*
|
||||
* @param serialModel serialModel
|
||||
* @return model
|
||||
*/
|
||||
M parse(String serialModel);
|
||||
|
||||
/**
|
||||
* 转为视图配置
|
||||
*
|
||||
* @param model model
|
||||
*/
|
||||
void toView(M model);
|
||||
|
||||
/**
|
||||
* 转为视图配置
|
||||
*
|
||||
* @param serialModel serialModel
|
||||
* @return model
|
||||
*/
|
||||
M toView(String serialModel);
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import cd.casic.module.terminal.common.holder.SpringHolder;
|
||||
|
||||
/**
|
||||
* 标准数据定义
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/21 0:07
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public interface GenericsStrategyDefinition {
|
||||
|
||||
/**
|
||||
* 获取数据处理策略
|
||||
*
|
||||
* @return class
|
||||
*/
|
||||
Class<? extends GenericsDataStrategy<? extends GenericsDataModel>> getStrategyClass();
|
||||
|
||||
/**
|
||||
* 获取数据模型策略处理器
|
||||
*
|
||||
* @param <M> Model
|
||||
* @param <S> Strategy
|
||||
* @return Strategy Bean
|
||||
*/
|
||||
default <M extends GenericsDataModel, S extends GenericsDataStrategy<M>> S getStrategy() {
|
||||
return (S) SpringHolder.getBean(this.getStrategyClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认值
|
||||
*
|
||||
* @param <M> model
|
||||
* @return model
|
||||
*/
|
||||
default <M extends GenericsDataModel> M getDefault() {
|
||||
return (M) this.getStrategy().getDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行完整验证链
|
||||
*
|
||||
* @param beforeModel beforeModel
|
||||
* @param afterModel afterModel
|
||||
*/
|
||||
default void doValid(GenericsDataModel beforeModel, GenericsDataModel afterModel) {
|
||||
this.getStrategy().doValid(beforeModel, afterModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化对象
|
||||
*
|
||||
* @param serialModel serialModel
|
||||
* @param <M> model
|
||||
* @return model
|
||||
*/
|
||||
default <M extends GenericsDataModel> M parse(String serialModel) {
|
||||
return (M) this.getStrategy().parse(serialModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为视图对象
|
||||
*
|
||||
* @param serialModel serialModel
|
||||
* @param <M> model
|
||||
* @return viewModel
|
||||
*/
|
||||
default <M extends GenericsDataModel> M toView(String serialModel) {
|
||||
return (M) this.getStrategy().toView(serialModel);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Arrays1;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import cd.casic.module.terminal.common.annotation.Module;
|
||||
|
||||
|
||||
/**
|
||||
* 操作类型初始化器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/10/13 17:45
|
||||
*/
|
||||
public abstract class InitializingOperatorTypes implements OperatorTypeDefinition {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
// 获取模块注解
|
||||
Module moduleDefinition = this.getClass().getAnnotation(Module.class);
|
||||
if (moduleDefinition == null) {
|
||||
return;
|
||||
}
|
||||
// 获取类型
|
||||
OperatorType[] types = this.types();
|
||||
if (Arrays1.isEmpty(types)) {
|
||||
return;
|
||||
}
|
||||
// 定义类型
|
||||
String module = moduleDefinition.value();
|
||||
for (OperatorType type : types) {
|
||||
type.setModule(module);
|
||||
OperatorTypeHolder.set(type);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 操作日志模型
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/10/9 18:44
|
||||
*/
|
||||
@Data
|
||||
public class OperatorLogModel{
|
||||
|
||||
/**
|
||||
* userId
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* traceId
|
||||
*/
|
||||
private String traceId;
|
||||
|
||||
/**
|
||||
* 请求 ip
|
||||
*/
|
||||
private String address;
|
||||
|
||||
/**
|
||||
* 请求地址
|
||||
*/
|
||||
private String location;
|
||||
|
||||
/**
|
||||
* user-agent
|
||||
*/
|
||||
private String userAgent;
|
||||
|
||||
/**
|
||||
* 日志
|
||||
*/
|
||||
private String logInfo;
|
||||
|
||||
/**
|
||||
* 风险等级
|
||||
*/
|
||||
private String riskLevel;
|
||||
|
||||
/**
|
||||
* 模块
|
||||
*/
|
||||
private String module;
|
||||
|
||||
/**
|
||||
* 操作类型
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
private String extra;
|
||||
|
||||
/**
|
||||
* 操作结果 0失败 1成功
|
||||
*/
|
||||
private Integer result;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private String errorMessage;
|
||||
|
||||
/**
|
||||
* 返回值
|
||||
*/
|
||||
private String returnValue;
|
||||
|
||||
/**
|
||||
* 操作时间
|
||||
*/
|
||||
private Integer duration;
|
||||
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private Date startTime;
|
||||
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private Date endTime;
|
||||
|
||||
}
|
@ -0,0 +1,193 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import cd.casic.framework.security.core.LoginUser;
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.serializer.SerializeFilter;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 操作日志工具类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/10/10 11:32
|
||||
*/
|
||||
public class OperatorLogs implements ExtraFieldConst {
|
||||
|
||||
private static final String UN_SAVE_FLAG = "__un__save__";
|
||||
|
||||
private static SerializeFilter[] serializeFilters;
|
||||
|
||||
/**
|
||||
* 拓展信息
|
||||
*/
|
||||
private static final ThreadLocal<Map<String, Object>> EXTRA_HOLDER = ThreadLocal.withInitial(Maps::newMap);
|
||||
|
||||
/**
|
||||
* 当前用户 优先于登录用户
|
||||
*/
|
||||
private static final ThreadLocal<LoginUser> USER_HOLDER = new ThreadLocal<>();
|
||||
|
||||
private OperatorLogs() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加日志参数
|
||||
*
|
||||
* @param key key
|
||||
* @param value value
|
||||
*/
|
||||
public static void add(String key, Object value) {
|
||||
EXTRA_HOLDER.get().put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加日志参数 json
|
||||
*
|
||||
* @param key key
|
||||
* @param value value
|
||||
*/
|
||||
public static void addJson(String key, Object value) {
|
||||
EXTRA_HOLDER.get().put(key, JSON.parseObject(toJsonString(value)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加日志参数
|
||||
*
|
||||
* @param map map
|
||||
*/
|
||||
public static void add(Map<String, ?> map) {
|
||||
EXTRA_HOLDER.get().putAll(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加日志参数
|
||||
*
|
||||
* @param obj obj
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void add(Object obj) {
|
||||
if (obj == null) {
|
||||
return;
|
||||
}
|
||||
// if (obj instanceof JSONObject || obj instanceof com.alibaba.fastjson2.JSONObject) {
|
||||
if (obj instanceof JSONObject || obj instanceof JSONObject) {
|
||||
EXTRA_HOLDER.get().putAll(JSON.parseObject(toJsonString(obj)));
|
||||
} else if (obj instanceof Map) {
|
||||
EXTRA_HOLDER.get().putAll((Map<String, ?>) obj);
|
||||
} else {
|
||||
EXTRA_HOLDER.get().putAll(JSON.parseObject(toJsonString(obj)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 json
|
||||
*
|
||||
* @param value value
|
||||
* @return json
|
||||
*/
|
||||
public static String toJsonString(Object value) {
|
||||
return JSON.toJSONString(value, serializeFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置不保存
|
||||
*/
|
||||
public static void unSave() {
|
||||
setSave(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否保存
|
||||
*
|
||||
* @param save save
|
||||
*/
|
||||
public static void setSave(boolean save) {
|
||||
if (save) {
|
||||
EXTRA_HOLDER.get().remove(UN_SAVE_FLAG);
|
||||
} else {
|
||||
EXTRA_HOLDER.get().put(UN_SAVE_FLAG, UN_SAVE_FLAG);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否保存
|
||||
*
|
||||
* @return save
|
||||
*/
|
||||
public static boolean isSave() {
|
||||
return !UN_SAVE_FLAG.equals(EXTRA_HOLDER.get().get(UN_SAVE_FLAG));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取参数
|
||||
*
|
||||
* @return map
|
||||
*/
|
||||
public static Map<String, Object> get() {
|
||||
return EXTRA_HOLDER.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户信息
|
||||
*
|
||||
* @param user user
|
||||
*/
|
||||
public static void setUser(LoginUser user) {
|
||||
USER_HOLDER.set(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户
|
||||
*
|
||||
* @return user
|
||||
*/
|
||||
public static LoginUser getUser() {
|
||||
return USER_HOLDER.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空
|
||||
*/
|
||||
public static void clear() {
|
||||
EXTRA_HOLDER.remove();
|
||||
USER_HOLDER.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空 html tag
|
||||
*
|
||||
* @param log log
|
||||
* @return cleared
|
||||
*/
|
||||
public static String clearHtmlTag(String log) {
|
||||
if (Strings.isBlank(log)) {
|
||||
return log;
|
||||
}
|
||||
return log.replaceAll("<sb 0>", "")
|
||||
.replaceAll("<sb 2>", "")
|
||||
.replaceAll("<sb>", "")
|
||||
.replaceAll("</sb>", "")
|
||||
.replaceAll("<sr 0>", "")
|
||||
.replaceAll("<sr 2>", "")
|
||||
.replaceAll("<sr>", "")
|
||||
.replaceAll("</sr>", "")
|
||||
.replaceAll("<b>", "")
|
||||
.replaceAll("</b>", "")
|
||||
.replaceAll("<br/>", "\n");
|
||||
}
|
||||
|
||||
public static void setSerializeFilters(SerializeFilter[] serializeFilters) {
|
||||
if (OperatorLogs.serializeFilters != null) {
|
||||
// unmodified
|
||||
throw Exceptions.state();
|
||||
}
|
||||
OperatorLogs.serializeFilters = serializeFilters;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 操作风险等级
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/10/12 14:19
|
||||
*/
|
||||
@Getter
|
||||
public enum OperatorRiskLevel {
|
||||
|
||||
/**
|
||||
* 低风险
|
||||
*/
|
||||
L,
|
||||
|
||||
/**
|
||||
* 中风险
|
||||
*/
|
||||
M,
|
||||
|
||||
/**
|
||||
* 高风险
|
||||
*/
|
||||
H,
|
||||
|
||||
;
|
||||
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 操作类型定义
|
||||
* <p>
|
||||
* 因为枚举需要实现 注解中不可以使用 则需要使用对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/10/10 10:29
|
||||
*/
|
||||
@Getter
|
||||
public class OperatorType {
|
||||
|
||||
/**
|
||||
* 风险等级
|
||||
*/
|
||||
private final OperatorRiskLevel riskLevel;
|
||||
|
||||
/**
|
||||
* 模块
|
||||
*/
|
||||
private String module;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final String type;
|
||||
|
||||
/**
|
||||
* 模板
|
||||
*/
|
||||
private final String template;
|
||||
|
||||
public OperatorType(OperatorRiskLevel riskLevel, String type, String template) {
|
||||
this(riskLevel, null, type, template);
|
||||
}
|
||||
|
||||
public OperatorType(OperatorRiskLevel riskLevel, String module, String type, String template) {
|
||||
this.riskLevel = riskLevel;
|
||||
this.module = module;
|
||||
this.type = type;
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
public void setModule(String module) {
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
|
||||
/**
|
||||
* 操作类型定义
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/10/13 18:23
|
||||
*/
|
||||
public interface OperatorTypeDefinition {
|
||||
|
||||
/**
|
||||
* 获取操作类型
|
||||
*
|
||||
* @return 操作类型
|
||||
*/
|
||||
OperatorType[] types();
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 操作日志类型实例
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/10/10 14:43
|
||||
*/
|
||||
public class OperatorTypeHolder {
|
||||
|
||||
private static final Map<String, OperatorType> TYPES = new HashMap<>();
|
||||
|
||||
private OperatorTypeHolder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型
|
||||
*
|
||||
* @param key key
|
||||
* @return type
|
||||
*/
|
||||
public static OperatorType get(String key) {
|
||||
return TYPES.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置类型
|
||||
*
|
||||
* @param type type
|
||||
*/
|
||||
public static void set(OperatorType type) {
|
||||
TYPES.put(type.getType(), type);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
/**
|
||||
* rsa 解密器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/1/5 20:58
|
||||
*/
|
||||
public interface RsaDecryptor {
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param value value
|
||||
* @return value
|
||||
*/
|
||||
String decrypt(String value);
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import cd.casic.module.terminal.common.config.ConfigRef;
|
||||
import cd.casic.module.terminal.common.config.ConfigStore;
|
||||
import cd.casic.module.terminal.common.constant.ConfigKeys;
|
||||
import cn.orionsec.kit.lang.utils.crypto.RSA;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* rsa 加密器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/1/7 11:32
|
||||
*/
|
||||
public class RsaDecryptorImpl implements RsaDecryptor {
|
||||
|
||||
private static final String SPLIT = "\\|";
|
||||
|
||||
private final ConfigRef<RSAPrivateKey> privateKey;
|
||||
|
||||
public RsaDecryptorImpl(ConfigStore configStore) {
|
||||
this.privateKey = configStore.ref(ConfigKeys.ENCRYPT_PRIVATE_KEY, RSA::getPrivateKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String value) {
|
||||
return Arrays.stream(value.split(SPLIT))
|
||||
.map(s -> RSA.decrypt(s, privateKey.value))
|
||||
.collect(Collectors.joining());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import cn.orionsec.kit.lang.id.UUIds;
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import org.slf4j.MDC;
|
||||
|
||||
/**
|
||||
* traceId 持有者
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/6/16 17:35
|
||||
*/
|
||||
public class TraceIdHolder {
|
||||
|
||||
public static final String TRACE_ID_HEADER = "trace-id";
|
||||
|
||||
public static final String TRACE_ID_MDC = "tid";
|
||||
|
||||
private TraceIdHolder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求序列
|
||||
*/
|
||||
private static final ThreadLocal<String> HOLDER = new TransmittableThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 获取 traceId
|
||||
*
|
||||
* @return traceId
|
||||
*/
|
||||
public static String get() {
|
||||
return HOLDER.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 traceId
|
||||
*/
|
||||
public static void set() {
|
||||
set(createTraceId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 traceId
|
||||
*
|
||||
* @param traceId traceId
|
||||
*/
|
||||
public static void set(String traceId) {
|
||||
// 设置应用上下文
|
||||
HOLDER.set(traceId);
|
||||
// 设置日志上下文
|
||||
setMdc(traceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 traceId
|
||||
*/
|
||||
public static void remove() {
|
||||
// 移除应用上下文
|
||||
HOLDER.remove();
|
||||
// 移除日志上下文
|
||||
removeMdc();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从应用上下文 设置到日志上下文
|
||||
*/
|
||||
public static void setMdc() {
|
||||
setMdc(HOLDER.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置到日志上下文
|
||||
*
|
||||
* @param traceId traceId
|
||||
*/
|
||||
public static void setMdc(String traceId) {
|
||||
MDC.put(TRACE_ID_MDC, traceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除日志上下文
|
||||
*/
|
||||
public static void removeMdc() {
|
||||
MDC.remove(TRACE_ID_MDC);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 traceId
|
||||
*
|
||||
* @return traceId
|
||||
*/
|
||||
public static String createTraceId() {
|
||||
return UUIds.random32();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 更新密码操作
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/9/21 11:32
|
||||
*/
|
||||
public interface UpdatePasswordAction extends Serializable {
|
||||
|
||||
/**
|
||||
* 是否使用新密码
|
||||
*
|
||||
* @return use
|
||||
*/
|
||||
Boolean getUseNewPassword();
|
||||
|
||||
/**
|
||||
* 获取密码
|
||||
*
|
||||
* @return password
|
||||
*/
|
||||
String getPassword();
|
||||
|
||||
/**
|
||||
* 设置密码
|
||||
*
|
||||
* @param password password
|
||||
*/
|
||||
void setPassword(String password);
|
||||
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.WebSocketExtension;
|
||||
import org.springframework.web.socket.WebSocketMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* web socket 同步会话
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/5/20 10:12
|
||||
*/
|
||||
public class WebSocketSyncSession implements WebSocketSession {
|
||||
|
||||
private final WebSocketSession delegate;
|
||||
|
||||
public WebSocketSyncSession(WebSocketSession delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.delegate.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
return this.delegate.getUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHandshakeHeaders() {
|
||||
return this.delegate.getHandshakeHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAttributes() {
|
||||
return this.delegate.getAttributes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getPrincipal() {
|
||||
return this.delegate.getPrincipal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getLocalAddress() {
|
||||
return this.delegate.getLocalAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getRemoteAddress() {
|
||||
return this.delegate.getRemoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAcceptedProtocol() {
|
||||
return this.delegate.getAcceptedProtocol();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTextMessageSizeLimit(int messageSizeLimit) {
|
||||
this.delegate.setTextMessageSizeLimit(messageSizeLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTextMessageSizeLimit() {
|
||||
return this.delegate.getTextMessageSizeLimit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBinaryMessageSizeLimit(int messageSizeLimit) {
|
||||
this.delegate.setBinaryMessageSizeLimit(messageSizeLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBinaryMessageSizeLimit() {
|
||||
return this.delegate.getBinaryMessageSizeLimit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<WebSocketExtension> getExtensions() {
|
||||
return this.delegate.getExtensions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(WebSocketMessage<?> message) throws IOException {
|
||||
synchronized (this.delegate) {
|
||||
this.delegate.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return this.delegate.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(CloseStatus status) throws IOException {
|
||||
this.delegate.close(status);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import cn.orionsec.kit.lang.utils.Threads;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* websocket 工具类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2021/6/14 0:36
|
||||
*/
|
||||
@Slf4j
|
||||
public class WebSockets {
|
||||
|
||||
private WebSockets() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建同步会话
|
||||
*
|
||||
* @param session session
|
||||
* @return session
|
||||
*/
|
||||
public static WebSocketSession createSyncSession(WebSocketSession session) {
|
||||
return new WebSocketSyncSession(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取属性
|
||||
*
|
||||
* @param channel channel
|
||||
* @param attr attr
|
||||
* @param <E> T
|
||||
* @return T
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E getAttr(WebSocketSession channel, String attr) {
|
||||
return (E) channel.getAttributes().get(attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息 忽略并发报错
|
||||
*
|
||||
* @param session session
|
||||
* @param message message
|
||||
*/
|
||||
public static void sendJson(WebSocketSession session, Object message) {
|
||||
sendText(session, JSON.toJSONString(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息 忽略并发报错
|
||||
*
|
||||
* @param session session
|
||||
* @param message message
|
||||
*/
|
||||
public static void sendText(WebSocketSession session, String message) {
|
||||
if (!session.isOpen()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (session instanceof WebSocketSyncSession) {
|
||||
// 发送消息
|
||||
session.sendMessage(new TextMessage(message));
|
||||
} else {
|
||||
synchronized (session) {
|
||||
// 发送消息
|
||||
session.sendMessage(new TextMessage(message));
|
||||
}
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
// 并发异常
|
||||
log.error("发送消息失败, 准备进行重试 {}", Exceptions.getDigest(e));
|
||||
// 并发重试
|
||||
retrySendText(session, message, Const.MS_100);
|
||||
} catch (IOException e) {
|
||||
throw Exceptions.ioRuntime(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重试发送消息 忽略并发报错
|
||||
*
|
||||
* @param session session
|
||||
* @param message message
|
||||
* @param delay delay
|
||||
*/
|
||||
public static void retrySendText(WebSocketSession session, String message, long delay) {
|
||||
if (!session.isOpen()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Threads.sleep(delay);
|
||||
session.sendMessage(new TextMessage(message));
|
||||
log.info("消息重发成功");
|
||||
Threads.sleep(delay);
|
||||
} catch (Exception ex) {
|
||||
throw Exceptions.ioRuntime(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭会话
|
||||
*
|
||||
* @param session session
|
||||
* @param code code
|
||||
*/
|
||||
public static void close(WebSocketSession session, WsCloseCode code) {
|
||||
if (!session.isOpen()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
session.close(new CloseStatus(code.getCode(), code.getReason()));
|
||||
} catch (Exception e) {
|
||||
log.error("websocket close failure", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
|
||||
package cd.casic.module.terminal.common;
|
||||
|
||||
/**
|
||||
* ws 服务端关闭 code
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2021/6/16 15:18
|
||||
*/
|
||||
public interface WsCloseCode {
|
||||
|
||||
/**
|
||||
* code
|
||||
*
|
||||
* @return code
|
||||
*/
|
||||
int getCode();
|
||||
|
||||
/**
|
||||
* reason
|
||||
*
|
||||
* @return reason
|
||||
*/
|
||||
String getReason();
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package cd.casic.module.terminal.common.annotation;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 模块
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/10/9 18:44
|
||||
*/
|
||||
@Component
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Module {
|
||||
|
||||
/**
|
||||
* 模块
|
||||
*/
|
||||
String value();
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package cd.casic.module.terminal.common.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 配置引用
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/1/6 18:01
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class ConfigRef<T> {
|
||||
|
||||
public final String key;
|
||||
|
||||
public T value;
|
||||
|
||||
protected final Function<String, T> convert;
|
||||
|
||||
public ConfigRef(String key, Function<String, T> convert) {
|
||||
this.key = key;
|
||||
this.convert = convert;
|
||||
}
|
||||
|
||||
/**
|
||||
* 覆盖配置
|
||||
*
|
||||
* @param value value
|
||||
*/
|
||||
public abstract void override(String value);
|
||||
|
||||
/**
|
||||
* 修改配置
|
||||
*
|
||||
* @param value value
|
||||
*/
|
||||
public abstract void set(T value);
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*
|
||||
* @return value
|
||||
*/
|
||||
public abstract T get();
|
||||
|
||||
/**
|
||||
* 修改回调
|
||||
*
|
||||
* @param changeEvent changeEvent
|
||||
* @return this
|
||||
*/
|
||||
public abstract ConfigRef<T> onChange(BiConsumer<T, T> changeEvent);
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
|
||||
package cd.casic.module.terminal.common.config;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.Objects1;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 配置引用实现类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/1/14 16:10
|
||||
*/
|
||||
@Slf4j
|
||||
public class ConfigRefImpl<T> extends ConfigRef<T> {
|
||||
|
||||
protected BiConsumer<T, T> changeEvent;
|
||||
|
||||
public ConfigRefImpl(String key, Function<String, T> convert) {
|
||||
super(key, convert);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void override(String value) {
|
||||
try {
|
||||
this.set(convert.apply(value));
|
||||
} catch (Exception e) {
|
||||
log.error("ConfigRef trigger override error key: {}, value: {}", key, value, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T value) {
|
||||
T before = this.value;
|
||||
this.value = value;
|
||||
// 被修改
|
||||
if (!Objects1.eq(before, value)) {
|
||||
log.info("ConfigRef changed key: {}, value: {}", key, value);
|
||||
// 触发事件
|
||||
if (changeEvent != null) {
|
||||
changeEvent.accept(value, before);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRef<T> onChange(BiConsumer<T, T> changeEvent) {
|
||||
this.changeEvent = changeEvent;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,244 @@
|
||||
|
||||
package cd.casic.module.terminal.common.config;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 配置中心
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/1/5 21:30
|
||||
*/
|
||||
public interface ConfigStore {
|
||||
|
||||
/**
|
||||
* 获取 string 配置
|
||||
*
|
||||
* @param key key
|
||||
* @return config
|
||||
*/
|
||||
String getString(String key);
|
||||
|
||||
/**
|
||||
* 获取 string 配置
|
||||
*
|
||||
* @param key key
|
||||
* @param defaultValue defaultValue
|
||||
* @return config
|
||||
*/
|
||||
String getString(String key, String defaultValue);
|
||||
|
||||
/**
|
||||
* 获取 int 配置
|
||||
*
|
||||
* @param key key
|
||||
* @return config
|
||||
*/
|
||||
Integer getInteger(String key);
|
||||
|
||||
/**
|
||||
* 获取 int 配置
|
||||
*
|
||||
* @param key key
|
||||
* @param defaultValue defaultValue
|
||||
* @return config
|
||||
*/
|
||||
Integer getInteger(String key, Integer defaultValue);
|
||||
|
||||
/**
|
||||
* 获取 long 配置
|
||||
*
|
||||
* @param key key
|
||||
* @return config
|
||||
*/
|
||||
Long getLong(String key);
|
||||
|
||||
/**
|
||||
* 获取 long 配置
|
||||
*
|
||||
* @param key key
|
||||
* @param defaultValue defaultValue
|
||||
* @return config
|
||||
*/
|
||||
Long getLong(String key, Long defaultValue);
|
||||
|
||||
/**
|
||||
* 获取 double 配置
|
||||
*
|
||||
* @param key key
|
||||
* @return config
|
||||
*/
|
||||
Double getDouble(String key);
|
||||
|
||||
/**
|
||||
* 获取 double 配置
|
||||
*
|
||||
* @param key key
|
||||
* @param defaultValue defaultValue
|
||||
* @return config
|
||||
*/
|
||||
Double getDouble(String key, Double defaultValue);
|
||||
|
||||
/**
|
||||
* 获取 boolean 配置
|
||||
*
|
||||
* @param key key
|
||||
* @return config
|
||||
*/
|
||||
Boolean getBoolean(String key);
|
||||
|
||||
/**
|
||||
* 获取 boolean 配置
|
||||
*
|
||||
* @param key key
|
||||
* @param defaultValue defaultValue
|
||||
* @return config
|
||||
*/
|
||||
Boolean getBoolean(String key, Boolean defaultValue);
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*
|
||||
* @param key key
|
||||
* @return conf
|
||||
*/
|
||||
String getConfig(String key);
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*
|
||||
* @param key key
|
||||
* @param defaultValue defaultValue
|
||||
* @return conf
|
||||
*/
|
||||
String getConfig(String key, String defaultValue);
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*
|
||||
* @param key key
|
||||
* @param convert convert
|
||||
* @param <T> T
|
||||
* @return conf
|
||||
*/
|
||||
<T> T getConfig(String key, Function<String, T> convert);
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*
|
||||
* @param key key
|
||||
* @param convert convert
|
||||
* @param defaultValue defaultValue
|
||||
* @param <T> T
|
||||
* @return conf
|
||||
*/
|
||||
<T> T getConfig(String key, Function<String, T> convert, T defaultValue);
|
||||
|
||||
/**
|
||||
* 获取 string 配置
|
||||
*
|
||||
* @param key key
|
||||
* @return ref
|
||||
*/
|
||||
ConfigRef<String> string(String key);
|
||||
|
||||
/**
|
||||
* 获取 string 配置
|
||||
*
|
||||
* @param key key
|
||||
* @param defaultValue defaultValue
|
||||
* @return ref
|
||||
*/
|
||||
ConfigRef<String> string(String key, String defaultValue);
|
||||
|
||||
/**
|
||||
* 获取 int 配置
|
||||
*
|
||||
* @param key key
|
||||
* @return ref
|
||||
*/
|
||||
ConfigRef<Integer> int32(String key);
|
||||
|
||||
/**
|
||||
* 获取 int 配置
|
||||
*
|
||||
* @param key key
|
||||
* @param defaultValue defaultValue
|
||||
* @return ref
|
||||
*/
|
||||
ConfigRef<Integer> int32(String key, Integer defaultValue);
|
||||
|
||||
/**
|
||||
* 获取 long 配置
|
||||
*
|
||||
* @param key key
|
||||
* @return ref
|
||||
*/
|
||||
ConfigRef<Long> int64(String key);
|
||||
|
||||
/**
|
||||
* 获取 long 配置
|
||||
*
|
||||
* @param key key
|
||||
* @param defaultValue defaultValue
|
||||
* @return ref
|
||||
*/
|
||||
ConfigRef<Long> int64(String key, Long defaultValue);
|
||||
|
||||
/**
|
||||
* 获取 double 配置
|
||||
*
|
||||
* @param key key
|
||||
* @return ref
|
||||
*/
|
||||
ConfigRef<Double> float64(String key);
|
||||
|
||||
/**
|
||||
* 获取 double 配置
|
||||
*
|
||||
* @param key key
|
||||
* @param defaultValue defaultValue
|
||||
* @return ref
|
||||
*/
|
||||
ConfigRef<Double> float64(String key, Double defaultValue);
|
||||
|
||||
/**
|
||||
* 获取 boolean 配置
|
||||
*
|
||||
* @param key key
|
||||
* @return ref
|
||||
*/
|
||||
ConfigRef<Boolean> bool(String key);
|
||||
|
||||
/**
|
||||
* 获取 boolean 配置
|
||||
*
|
||||
* @param key key
|
||||
* @param defaultValue defaultValue
|
||||
* @return ref
|
||||
*/
|
||||
ConfigRef<Boolean> bool(String key, Boolean defaultValue);
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*
|
||||
* @param key key
|
||||
* @param convert convert
|
||||
* @param <T> T
|
||||
* @return ref
|
||||
*/
|
||||
<T> ConfigRef<T> ref(String key, Function<String, T> convert);
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*
|
||||
* @param key key
|
||||
* @param convert convert
|
||||
* @param defaultValue defaultValue
|
||||
* @param <T> T
|
||||
* @return ref
|
||||
*/
|
||||
<T> ConfigRef<T> ref(String key, Function<String, T> convert, T defaultValue);
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
|
||||
package cd.casic.module.terminal.common.config;
|
||||
|
||||
/**
|
||||
* 可控配置中心
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/1/6 23:00
|
||||
*/
|
||||
public interface ManagementConfigStore extends ConfigStore {
|
||||
|
||||
/**
|
||||
* 加载全部配置
|
||||
*/
|
||||
void loadAllConfig();
|
||||
|
||||
/**
|
||||
* 覆盖配置
|
||||
*
|
||||
* @param key key
|
||||
* @param value value
|
||||
*/
|
||||
void override(String key, String value);
|
||||
|
||||
/**
|
||||
* 注册 ref
|
||||
*
|
||||
* @param ref ref
|
||||
*/
|
||||
void register(ConfigRef<?> ref);
|
||||
|
||||
}
|
@ -0,0 +1,202 @@
|
||||
|
||||
package cd.casic.module.terminal.common.config;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 配置中心实现
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/1/6 17:20
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ManagementConfigStoreImpl implements ManagementConfigStore {
|
||||
|
||||
private final ConcurrentHashMap<String, String> configMap = new ConcurrentHashMap<>();
|
||||
|
||||
private final ConcurrentHashMap<String, List<ConfigRef<?>>> configRefs = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
@Override
|
||||
public void loadAllConfig() {
|
||||
/* configMap.putAll(service.getAllConfig());*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void override(String key, String value) {
|
||||
log.info("ConfigStore.override key: {}, value: {}", key, value);
|
||||
// 修改配置
|
||||
configMap.put(key, value);
|
||||
// 修改引用
|
||||
List<ConfigRef<?>> refs = configRefs.get(key);
|
||||
if (!Lists.isEmpty(refs)) {
|
||||
refs.forEach(s -> s.override(value));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(ConfigRef<?> ref) {
|
||||
String key = ref.key;
|
||||
log.info("ConfigStore.register ref key: {}", key);
|
||||
// 注册引用
|
||||
configRefs.computeIfAbsent(key, k -> new ArrayList<>()).add(ref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String key) {
|
||||
return this.getConfig(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString(String key, String defaultValue) {
|
||||
return this.getConfig(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getInteger(String key) {
|
||||
return this.getConfig(key, Integer::valueOf, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getInteger(String key, Integer defaultValue) {
|
||||
return this.getConfig(key, Integer::valueOf, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLong(String key) {
|
||||
return this.getConfig(key, Long::valueOf, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getLong(String key, Long defaultValue) {
|
||||
return this.getConfig(key, Long::valueOf, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getDouble(String key) {
|
||||
return this.getConfig(key, Double::valueOf, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getDouble(String key, Double defaultValue) {
|
||||
return this.getConfig(key, Double::valueOf, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getBoolean(String key) {
|
||||
return this.getConfig(key, Boolean::valueOf, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getBoolean(String key, Boolean defaultValue) {
|
||||
return this.getConfig(key, Boolean::valueOf, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfig(String key) {
|
||||
return this.getConfig(key, Function.identity(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getConfig(String key, String defaultValue) {
|
||||
return this.getConfig(key, Function.identity(), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getConfig(String key, Function<String, T> convert) {
|
||||
return this.getConfig(key, convert, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getConfig(String key, Function<String, T> convert, T defaultValue) {
|
||||
// 获取配置
|
||||
String conf = configMap.get(key);
|
||||
// 默认值
|
||||
if (conf == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
// 转换
|
||||
return convert.apply(conf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRef<String> string(String key) {
|
||||
return this.ref(key, Function.identity(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRef<String> string(String key, String defaultValue) {
|
||||
return this.ref(key, Function.identity(), defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRef<Integer> int32(String key) {
|
||||
return this.ref(key, Integer::valueOf, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRef<Integer> int32(String key, Integer defaultValue) {
|
||||
return this.ref(key, Integer::valueOf, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRef<Long> int64(String key) {
|
||||
return this.ref(key, Long::valueOf, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRef<Long> int64(String key, Long defaultValue) {
|
||||
return this.ref(key, Long::valueOf, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRef<Double> float64(String key) {
|
||||
return this.ref(key, Double::valueOf, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRef<Double> float64(String key, Double defaultValue) {
|
||||
return this.ref(key, Double::valueOf, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRef<Boolean> bool(String key) {
|
||||
return this.ref(key, Boolean::valueOf, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigRef<Boolean> bool(String key, Boolean defaultValue) {
|
||||
return this.ref(key, Boolean::valueOf, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ConfigRef<T> ref(String key, Function<String, T> convert) {
|
||||
return this.ref(key, convert, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ConfigRef<T> ref(String key, Function<String, T> convert, T defaultValue) {
|
||||
// 创建引用
|
||||
ConfigRef<T> ref = new ConfigRefImpl<>(key, convert);
|
||||
// 设置值
|
||||
String value = configMap.get(key);
|
||||
if (value != null) {
|
||||
ref.override(value);
|
||||
} else {
|
||||
ref.set(defaultValue);
|
||||
}
|
||||
// 注册引用
|
||||
this.register(ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
|
||||
package cd.casic.module.terminal.common.constant;
|
||||
|
||||
/**
|
||||
* 配置项常量
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/1/14 16:15
|
||||
*/
|
||||
public interface ConfigKeys {
|
||||
|
||||
/**
|
||||
* SFTP 文件预览大小
|
||||
*/
|
||||
String SFTP_PREVIEW_SIZE = "sftp_previewSize";
|
||||
|
||||
/**
|
||||
* SFTP 重复文件备份
|
||||
*/
|
||||
String SFTP_UPLOAD_PRESENT_BACKUP = "sftp_uploadPresentBackup";
|
||||
|
||||
/**
|
||||
* SFTP 备份文件名称
|
||||
*/
|
||||
String SFTP_UPLOAD_BACKUP_FILE_NAME = "sftp_uploadBackupFileName";
|
||||
|
||||
|
||||
/**
|
||||
* 加密私钥
|
||||
*/
|
||||
String ENCRYPT_PRIVATE_KEY = "encrypt_privateKey";
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
package cd.casic.module.terminal.common.holder;
|
||||
|
||||
import cd.casic.module.terminal.utils.Valid;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
*
|
||||
* bean 工具类
|
||||
* @author Yuru Pu
|
||||
* @version 1.0
|
||||
* @since 2025/7/8 17:44
|
||||
*/
|
||||
public class SpringHolder {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(SpringHolder.class);
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
private static ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private SpringHolder() {
|
||||
}
|
||||
|
||||
public static class ApplicationContextAwareStore implements ApplicationContextAware, BeanFactoryPostProcessor {
|
||||
|
||||
public ApplicationContextAwareStore() {
|
||||
LOGGER.info("init spring holder");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
LOGGER.info("inject spring holder ApplicationContext");
|
||||
SpringHolder.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
|
||||
LOGGER.info("inject spring holder BeanFactory");
|
||||
SpringHolder.beanFactory = configurableListableBeanFactory;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上下文容器
|
||||
*
|
||||
* @return ignore
|
||||
*/
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
public static ConfigurableListableBeanFactory getBeanFactory() {
|
||||
return beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布事件
|
||||
*
|
||||
* @param event event
|
||||
*/
|
||||
public static void publishEvent(ApplicationEvent event) {
|
||||
applicationContext.publishEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布事件
|
||||
*
|
||||
* @param event event
|
||||
*/
|
||||
public static void publishEvent(Object event) {
|
||||
applicationContext.publishEvent(event);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getBean(String beanName) {
|
||||
return ((T) applicationContext.getBean(beanName));
|
||||
}
|
||||
|
||||
public static <T> T getBean(Class<T> type) {
|
||||
return applicationContext.getBean(type);
|
||||
}
|
||||
|
||||
public static <T> T getBean(String beanName, Class<T> type) {
|
||||
return applicationContext.getBean(beanName, type);
|
||||
}
|
||||
|
||||
public static boolean containsBean(String beanName) {
|
||||
return applicationContext.containsBean(beanName);
|
||||
}
|
||||
|
||||
public static String[] getBeanNames() {
|
||||
return applicationContext.getBeanDefinitionNames();
|
||||
}
|
||||
|
||||
public static boolean isSingletonBean(String beanName) {
|
||||
return applicationContext.isSingleton(beanName);
|
||||
}
|
||||
|
||||
public static Class<?> getType(String beanName) {
|
||||
return applicationContext.getType(beanName);
|
||||
}
|
||||
|
||||
public static boolean isType(String beanName, Class<?> beanType) {
|
||||
return applicationContext.isTypeMatch(beanName, beanType);
|
||||
}
|
||||
|
||||
public static String[] getBeanAliases(String beanName) {
|
||||
return applicationContext.getAliases(beanName);
|
||||
}
|
||||
|
||||
public static void close() {
|
||||
if (applicationContext instanceof ConfigurableApplicationContext) {
|
||||
((ConfigurableApplicationContext) applicationContext).close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void refresh() {
|
||||
Valid.isInstanceOf(applicationContext, ConfigurableApplicationContext.class);
|
||||
ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) SpringHolder.applicationContext;
|
||||
applicationContext.refresh();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package cd.casic.module.terminal.configuration;
|
||||
|
||||
import cd.casic.module.terminal.common.holder.SpringHolder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
*
|
||||
* spring 配置类
|
||||
*
|
||||
* @author Yuru Pu
|
||||
* @version 1.0
|
||||
* @since 2025/7/10 14:46
|
||||
*/
|
||||
@Configuration
|
||||
public class SpringConfiguration {
|
||||
|
||||
/**
|
||||
* @return spring 容器工具类
|
||||
*/
|
||||
@Bean
|
||||
public SpringHolder.ApplicationContextAwareStore springHolderAware() {
|
||||
return new SpringHolder.ApplicationContextAwareStore();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package cd.casic.module.terminal.controller;
|
||||
|
||||
import cd.casic.framework.commons.pojo.CommonResult;
|
||||
import cd.casic.module.terminal.service.impl.TerminalService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import static cd.casic.framework.commons.pojo.CommonResult.success;
|
||||
|
||||
/**
|
||||
*
|
||||
* 终端连接
|
||||
* @author Yuru Pu
|
||||
* @version 1.0
|
||||
* @since 2025/7/8 11:36
|
||||
*/
|
||||
@Tag(name = "管理后台 - 终端")
|
||||
@RestController
|
||||
@RequestMapping("/asset/terminal")
|
||||
@Validated
|
||||
public class TerminalController {
|
||||
|
||||
@Resource
|
||||
private TerminalService terminalService;
|
||||
|
||||
@GetMapping("/access")
|
||||
@Operation(summary = "获取终端key")
|
||||
@PreAuthorize("@ss.hasPermission('terminal:access')")
|
||||
public CommonResult getTerminalAccessToken() {
|
||||
return success(terminalService.getTerminalAccessToken());
|
||||
}
|
||||
|
||||
@GetMapping("/transfer")
|
||||
@Operation(summary = "获取终端 transferToken")
|
||||
@PreAuthorize("@ss.hasPermission('asset:terminal:access')")
|
||||
public CommonResult<String> getTerminalTransferToken() {
|
||||
return success(terminalService.getTerminalTransferToken());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,63 @@
|
||||
package cd.casic.module.terminal.controller;
|
||||
|
||||
import cd.casic.module.terminal.service.TerminalSftpService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* SFTP 操作服务 api
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-12-26 22:09
|
||||
*/
|
||||
@Tag(name = "asset - SFTP 操作服务")
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RestController
|
||||
@RequestMapping("/asset/terminal-sftp")
|
||||
public class TerminalSftpController {
|
||||
|
||||
@Resource
|
||||
private TerminalSftpService terminalSftpService;
|
||||
|
||||
|
||||
@GetMapping("/get-content")
|
||||
@Operation(summary = "获取文件内容")
|
||||
@Parameter(name = "token", description = "token", required = true)
|
||||
public void getFileContentByToken(@RequestParam("token") String token, HttpServletResponse response) throws Exception {
|
||||
terminalSftpService.getFileContentByToken(token, response);
|
||||
}
|
||||
|
||||
@PostMapping("/set-content")
|
||||
@Operation(summary = "设置文件内容")
|
||||
@Parameter(name = "token", description = "token", required = true)
|
||||
@Parameter(name = "file", description = "file", required = true)
|
||||
public Boolean setFileContentByToken(@RequestParam("token") String token,
|
||||
@RequestParam("file") MultipartFile file) throws Exception {
|
||||
terminalSftpService.setFileContentByToken(token, file);
|
||||
return true;
|
||||
}
|
||||
|
||||
@PermitAll
|
||||
@GetMapping("/download")
|
||||
@Operation(summary = "下载文件")
|
||||
@Parameter(name = "channelId", description = "channelId", required = true)
|
||||
@Parameter(name = "transferToken", description = "transferToken", required = true)
|
||||
public StreamingResponseBody downloadWithTransferToken(@RequestParam("channelId") String channelId,
|
||||
@RequestParam("transferToken") String transferToken,
|
||||
HttpServletResponse response) {
|
||||
return terminalSftpService.downloadWithTransferToken(channelId, transferToken, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
package cd.casic.module.terminal.controller.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
|
||||
/**
|
||||
* 终端访问参数
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/26 15:47
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
//@DesensitizeObject 、、todo 自动脱敏的注解
|
||||
@Schema(name = "TerminalAccessDTO", description = "终端访问参数")
|
||||
public class TerminalAccessDTO {
|
||||
|
||||
@Schema(description = "userId")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "username")
|
||||
private String username;
|
||||
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package cd.casic.module.terminal.controller.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
|
||||
/**
|
||||
* 终端连接参数
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/26 15:47
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
//@DesensitizeObject
|
||||
@Schema(name = "TerminalConnectDTO", description = "终端连接参数")
|
||||
public class TerminalConnectDTO {
|
||||
|
||||
@Schema(description = "logId")
|
||||
private Long logId;
|
||||
|
||||
@Schema(description = "连接类型")
|
||||
private String connectType;
|
||||
|
||||
@Schema(description = "hostId")
|
||||
private Long hostId;
|
||||
|
||||
@Schema(description = "hostName")
|
||||
private String hostName;
|
||||
|
||||
@Schema(description = "主机编码")
|
||||
private String hostCode;
|
||||
|
||||
@Schema(description = "主机地址")
|
||||
private String hostAddress;
|
||||
|
||||
@Schema(description = "主机端口")
|
||||
private Integer hostPort;
|
||||
|
||||
@Schema(description = "系统类型")
|
||||
private String osType;
|
||||
|
||||
@Schema(description = "系统架构")
|
||||
private String archType;
|
||||
|
||||
@Schema(description = "超时时间")
|
||||
private Integer timeout;
|
||||
|
||||
@Schema(description = "SSH输出编码")
|
||||
private String charset;
|
||||
|
||||
@Schema(description = "文件名称编码")
|
||||
private String fileNameCharset;
|
||||
|
||||
@Schema(description = "文件内容编码")
|
||||
private String fileContentCharset;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
// @Desensitize(toEmpty = true)
|
||||
@Schema(description = "密码")
|
||||
private String password;
|
||||
|
||||
@Schema(description = "密钥id")
|
||||
private Long keyId;
|
||||
|
||||
// @Desensitize(toEmpty = true)
|
||||
@Schema(description = "公钥文本")
|
||||
private String publicKey;
|
||||
|
||||
// @Desensitize(toEmpty = true)
|
||||
@Schema(description = "私钥文本")
|
||||
private String privateKey;
|
||||
|
||||
// @Desensitize(toEmpty = true)
|
||||
@Schema(description = "私钥密码")
|
||||
private String privateKeyPassword;
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package cd.casic.module.terminal.controller.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 终端传输参数
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/26 15:47
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
//@DesensitizeObject
|
||||
@Schema(name = "TerminalTransferDTO", description = "终端传输参数")
|
||||
public class TerminalTransferDTO {
|
||||
|
||||
@Schema(description = "userId")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "username")
|
||||
private String username;
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package cd.casic.module.terminal.controller.request;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.*;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
* 终端连接日志 查询请求对象
|
||||
*
|
||||
* @author Yuru Pu
|
||||
* @version 1.0
|
||||
* @since 2025/7/9 14:07
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "TerminalConnectLogQueryRequest", description = "终端连接日志 查询请求对象")
|
||||
public class TerminalConnectLogQueryRequest {
|
||||
|
||||
@NotNull(groups = Id.class)
|
||||
@Schema(description = "id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "id")
|
||||
private List<Long> idList;
|
||||
|
||||
@Schema(description = "用户id")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "主机id")
|
||||
private Long hostId;
|
||||
|
||||
@Size(max = 128)
|
||||
@Schema(description = "主机地址")
|
||||
private String hostAddress;
|
||||
|
||||
@Size(max = 16)
|
||||
@Schema(description = "类型")
|
||||
private String type;
|
||||
|
||||
@Size(max = 64)
|
||||
@Schema(description = "sessionId")
|
||||
private String sessionId;
|
||||
|
||||
@Size(max = 16)
|
||||
@Schema(description = "状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "开始时间-区间")
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date[] startTimeRange;
|
||||
|
||||
@Schema(description = "创建时间 <=")
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||
private Date createTimeLe;
|
||||
|
||||
@Schema(description = "状态")
|
||||
private List<String> statusList;
|
||||
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package cd.casic.module.terminal.controller.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 终端连接日志 视图响应对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023-12-26 22:09
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "TerminalConnectLogVO", description = "终端连接日志 视图响应对象")
|
||||
public class TerminalConnectLogVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "id")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户id")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "主机id")
|
||||
private Long hostId;
|
||||
|
||||
@Schema(description = "主机名称")
|
||||
private String hostName;
|
||||
|
||||
@Schema(description = "主机地址")
|
||||
private String hostAddress;
|
||||
|
||||
@Schema(description = "类型")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "sessionId")
|
||||
private String sessionId;
|
||||
|
||||
@Schema(description = "开始时间")
|
||||
private Date startTime;
|
||||
|
||||
@Schema(description = "结束时间")
|
||||
private Date endTime;
|
||||
|
||||
/*@Schema(description = "额外信息")
|
||||
private TerminalConnectLogExtraDTO extra;*/
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package cd.casic.module.terminal.convert;
|
||||
|
||||
|
||||
import cd.casic.module.terminal.dal.dataobject.TerminalConnectLogDO;
|
||||
import cd.casic.module.terminal.controller.dto.TerminalConnectDTO;
|
||||
import cd.casic.module.terminal.controller.request.TerminalConnectLogQueryRequest;
|
||||
import cd.casic.module.terminal.controller.vo.TerminalConnectLogVO;
|
||||
import cd.casic.module.terminal.host.terminal.model.request.host.TerminalConnectLogCreateRequest;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* 终端连接日志 内部对象转换器
|
||||
*
|
||||
* @author Yuru Pu
|
||||
* @version 1.0
|
||||
* @since 2025/7/8 13:54
|
||||
*/
|
||||
@Mapper
|
||||
public interface TerminalConnectLogConvert {
|
||||
|
||||
TerminalConnectLogConvert MAPPER = Mappers.getMapper(TerminalConnectLogConvert.class);
|
||||
|
||||
TerminalConnectLogDO to(TerminalConnectLogCreateRequest request);
|
||||
|
||||
TerminalConnectLogDO to(TerminalConnectLogQueryRequest request);
|
||||
|
||||
TerminalConnectLogVO to(TerminalConnectLogDO domain);
|
||||
|
||||
TerminalConnectLogCreateRequest to(TerminalConnectDTO dto);
|
||||
|
||||
List<TerminalConnectLogVO> to(List<TerminalConnectLogDO> list);
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package cd.casic.module.terminal.dal.dataobject;
|
||||
|
||||
import cd.casic.framework.commons.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 终端连接日志 实体对象
|
||||
*
|
||||
* @author Yuru Pu
|
||||
* @version 1.0
|
||||
* @since 2025/7/9 11:33
|
||||
*/
|
||||
@Data
|
||||
@TableName(value = "terminal_connect_log", autoResultMap = true)
|
||||
@KeySequence("terminal_connect_log_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TerminalConnectLogDO extends BaseDO {
|
||||
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户id")
|
||||
@TableField("user_id")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "用户名")
|
||||
@TableField("username")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "主机id")
|
||||
@TableField("host_id")
|
||||
private Long hostId;
|
||||
|
||||
@Schema(description = "主机名称")
|
||||
@TableField("host_name")
|
||||
private String hostName;
|
||||
|
||||
@Schema(description = "主机地址")
|
||||
@TableField("host_address")
|
||||
private String hostAddress;
|
||||
|
||||
@Schema(description = "类型")
|
||||
@TableField("type")
|
||||
private String type;
|
||||
|
||||
@Schema(description = "sessionId")
|
||||
@TableField("session_id")
|
||||
private String sessionId;
|
||||
|
||||
@Schema(description = "状态")
|
||||
@TableField("status")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "开始时间")
|
||||
@TableField("start_time")
|
||||
private Date startTime;
|
||||
|
||||
@Schema(description = "结束时间")
|
||||
@TableField("end_time")
|
||||
private Date endTime;
|
||||
|
||||
@Schema(description = "额外信息")
|
||||
@TableField("extra_info")
|
||||
private String extraInfo;
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package cd.casic.module.terminal.dal.mysql;
|
||||
|
||||
import cd.casic.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cd.casic.module.terminal.dal.dataobject.TerminalConnectLogDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
*
|
||||
* 终端连接日志 Mapper 接口
|
||||
*
|
||||
* @author Yuru Pu
|
||||
* @version 1.0
|
||||
* @since 2025/7/9 11:38
|
||||
*/
|
||||
@Mapper
|
||||
public interface TerminalConnectLogMapper extends BaseMapperX<TerminalConnectLogDO> {
|
||||
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
package cd.casic.module.terminal.dal.redis;
|
||||
|
||||
import cn.orionsec.kit.lang.define.cache.key.CacheKeyDefine;
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* @author: Paul
|
||||
* @create: 2025-07-10 15:31
|
||||
*/
|
||||
@Repository
|
||||
public class TerminalRedisDAO {
|
||||
|
||||
|
||||
@Autowired
|
||||
private StringRedisTemplate redisTemplate;
|
||||
|
||||
/**
|
||||
* 获取 json
|
||||
*
|
||||
* @param key key
|
||||
* @param define define
|
||||
* @param <T> T
|
||||
* @return T
|
||||
*/
|
||||
public <T> T getJson(String key, CacheKeyDefine define) {
|
||||
return (T) getJson(key, define.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 json
|
||||
*
|
||||
* @param key key
|
||||
* @param type type
|
||||
* @param <T> T
|
||||
* @return T
|
||||
*/
|
||||
public <T> T getJson(String key, Class<T> type) {
|
||||
String value = redisTemplate.opsForValue().get(key);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
return (T) JSON.parseObject(value, type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置值
|
||||
*
|
||||
* @param define define
|
||||
* @param value value
|
||||
*/
|
||||
public void set(CacheKeyDefine define, Object value) {
|
||||
set(define.getKey(), define, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置值
|
||||
*
|
||||
* @param key key
|
||||
* @param define define
|
||||
* @param value value
|
||||
*/
|
||||
public void set(String key, CacheKeyDefine define, Object value) {
|
||||
if (value == null) {
|
||||
value = Strings.EMPTY;
|
||||
}
|
||||
if (define == null || define.getTimeout() == 0) {
|
||||
// 不过期
|
||||
redisTemplate.opsForValue().set(key, value.toString());
|
||||
} else {
|
||||
// 过期
|
||||
redisTemplate.opsForValue().set(key, value.toString(),
|
||||
define.getTimeout(),
|
||||
define.getUnit());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 json
|
||||
*
|
||||
* @param key key
|
||||
* @param define define
|
||||
* @param value value
|
||||
*/
|
||||
public void setJson(String key, CacheKeyDefine define, Object value) {
|
||||
if (define == null || define.getTimeout() == 0) {
|
||||
// 不过期
|
||||
redisTemplate.opsForValue().set(key, JSON.toJSONString(value));
|
||||
} else {
|
||||
// 过期
|
||||
redisTemplate.opsForValue().set(key, JSON.toJSONString(value),
|
||||
define.getTimeout(),
|
||||
define.getUnit());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 key
|
||||
*
|
||||
* @param key key
|
||||
*/
|
||||
public void delete(String key) {
|
||||
redisTemplate.delete(key);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
package cd.casic.module.terminal.define;
|
||||
|
||||
import cn.orionsec.kit.lang.constant.Const;
|
||||
import cn.orionsec.kit.lang.define.thread.ExecutorBuilder;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* 资产线程池
|
||||
*
|
||||
*/
|
||||
public interface AssetThreadPools {
|
||||
|
||||
/**
|
||||
* 超时检查线程池
|
||||
*/
|
||||
ThreadPoolExecutor TIMEOUT_CHECK = ExecutorBuilder.create()
|
||||
.namedThreadFactory("timeout-check-")
|
||||
.corePoolSize(1)
|
||||
.maxPoolSize(Integer.MAX_VALUE)
|
||||
.keepAliveTime(Const.MS_S_60)
|
||||
.workQueue(new SynchronousQueue<>())
|
||||
.allowCoreThreadTimeout(true)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* terminal 标准输出线程池
|
||||
*/
|
||||
ThreadPoolExecutor TERMINAL_STDOUT = ExecutorBuilder.create()
|
||||
.namedThreadFactory("terminal-stdout-")
|
||||
.corePoolSize(1)
|
||||
.maxPoolSize(Integer.MAX_VALUE)
|
||||
.keepAliveTime(Const.MS_S_60)
|
||||
.workQueue(new SynchronousQueue<>())
|
||||
.allowCoreThreadTimeout(true)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* terminal 操作线程池
|
||||
*/
|
||||
ThreadPoolExecutor TERMINAL_OPERATOR = ExecutorBuilder.create()
|
||||
.namedThreadFactory("terminal-operator-")
|
||||
.corePoolSize(1)
|
||||
.maxPoolSize(Integer.MAX_VALUE)
|
||||
.keepAliveTime(Const.MS_S_60)
|
||||
.workQueue(new SynchronousQueue<>())
|
||||
.allowCoreThreadTimeout(true)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* 批量执行任务线程池
|
||||
*/
|
||||
ThreadPoolExecutor EXEC_TASK = ExecutorBuilder.create()
|
||||
.namedThreadFactory("exec-task-")
|
||||
.corePoolSize(1)
|
||||
.maxPoolSize(Integer.MAX_VALUE)
|
||||
.keepAliveTime(Const.MS_S_60)
|
||||
.workQueue(new SynchronousQueue<>())
|
||||
.allowCoreThreadTimeout(true)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* 批量执行主机命令线程池
|
||||
*/
|
||||
ThreadPoolExecutor EXEC_HOST = ExecutorBuilder.create()
|
||||
.namedThreadFactory("exec-host-")
|
||||
.corePoolSize(1)
|
||||
.maxPoolSize(Integer.MAX_VALUE)
|
||||
.keepAliveTime(Const.MS_S_60)
|
||||
.workQueue(new SynchronousQueue<>())
|
||||
.allowCoreThreadTimeout(true)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* 批量执行日志查看线程池
|
||||
*/
|
||||
ThreadPoolExecutor EXEC_LOG = ExecutorBuilder.create()
|
||||
.namedThreadFactory("exec-log-")
|
||||
.corePoolSize(1)
|
||||
.maxPoolSize(Integer.MAX_VALUE)
|
||||
.keepAliveTime(Const.MS_S_60)
|
||||
.workQueue(new SynchronousQueue<>())
|
||||
.allowCoreThreadTimeout(true)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* 批量上传任务线程池
|
||||
*/
|
||||
ThreadPoolExecutor UPLOAD_TASK = ExecutorBuilder.create()
|
||||
.namedThreadFactory("upload-task-")
|
||||
.corePoolSize(1)
|
||||
.maxPoolSize(Integer.MAX_VALUE)
|
||||
.keepAliveTime(Const.MS_S_60)
|
||||
.workQueue(new SynchronousQueue<>())
|
||||
.allowCoreThreadTimeout(true)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* 批量上传主机线程池
|
||||
*/
|
||||
ThreadPoolExecutor UPLOAD_HOST = ExecutorBuilder.create()
|
||||
.namedThreadFactory("upload-host-")
|
||||
.corePoolSize(1)
|
||||
.maxPoolSize(Integer.MAX_VALUE)
|
||||
.keepAliveTime(Const.MS_S_60)
|
||||
.workQueue(new SynchronousQueue<>())
|
||||
.allowCoreThreadTimeout(true)
|
||||
.build();
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package cd.casic.module.terminal.define.cache;
|
||||
|
||||
import cd.casic.module.terminal.controller.dto.TerminalAccessDTO;
|
||||
import cd.casic.module.terminal.controller.dto.TerminalTransferDTO;
|
||||
import cd.casic.module.terminal.host.terminal.dto.SftpGetContentCacheDTO;
|
||||
import cd.casic.module.terminal.host.terminal.dto.SftpSetContentCacheDTO;
|
||||
import cn.orionsec.kit.lang.define.cache.key.CacheKeyBuilder;
|
||||
import cn.orionsec.kit.lang.define.cache.key.CacheKeyDefine;
|
||||
import cn.orionsec.kit.lang.define.cache.key.struct.RedisCacheStruct;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
*
|
||||
* 终端服务缓存 key
|
||||
*
|
||||
* @author Yuru Pu
|
||||
* @version 1.0
|
||||
* @since 2025/7/9 14:17
|
||||
*/
|
||||
public interface TerminalCacheKeyDefine {
|
||||
|
||||
CacheKeyDefine TERMINAL_ACCESS = new CacheKeyBuilder()
|
||||
.key("terminal:access:{}")
|
||||
.desc("终端访问token ${token}")
|
||||
.type(TerminalAccessDTO.class)
|
||||
.struct(RedisCacheStruct.STRING)
|
||||
.timeout(3, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
CacheKeyDefine TERMINAL_TRANSFER = new CacheKeyBuilder()
|
||||
.key("terminal:transfer:{}")
|
||||
.desc("终端传输token ${token}")
|
||||
.type(TerminalTransferDTO.class)
|
||||
.struct(RedisCacheStruct.STRING)
|
||||
.timeout(3, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
CacheKeyDefine TERMINAL_SFTP_GET_CONTENT = new CacheKeyBuilder()
|
||||
.key("terminal:sftp:gc:{}")
|
||||
.desc("sftp 获取文件内容 ${token}")
|
||||
.type(SftpGetContentCacheDTO.class)
|
||||
.struct(RedisCacheStruct.STRING)
|
||||
.timeout(5, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
CacheKeyDefine TERMINAL_SFTP_SET_CONTENT = new CacheKeyBuilder()
|
||||
.key("terminal:sftp:sc:{}")
|
||||
.desc("sftp 设置文件内容 ${token}")
|
||||
.type(SftpSetContentCacheDTO.class)
|
||||
.struct(RedisCacheStruct.STRING)
|
||||
.timeout(5, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package cd.casic.module.terminal.define.config;
|
||||
|
||||
import cd.casic.module.terminal.common.config.ConfigRef;
|
||||
import cd.casic.module.terminal.common.config.ConfigStore;
|
||||
import cd.casic.module.terminal.common.constant.ConfigKeys;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 应用 sftp 配置
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/4/15 22:00
|
||||
*/
|
||||
@Component
|
||||
public class AppSftpConfig {
|
||||
|
||||
/**
|
||||
* 文件预览大小
|
||||
*/
|
||||
private final ConfigRef<Integer> previewSize;
|
||||
|
||||
/**
|
||||
* 重复文件备份
|
||||
*/
|
||||
private final ConfigRef<Boolean> uploadPresentBackup;
|
||||
|
||||
/**
|
||||
* 备份文件名称
|
||||
*/
|
||||
private final ConfigRef<String> uploadBackupFileName;
|
||||
|
||||
public AppSftpConfig(ConfigStore configStore) {
|
||||
this.previewSize = configStore.int32(ConfigKeys.SFTP_PREVIEW_SIZE);
|
||||
this.uploadPresentBackup = configStore.bool(ConfigKeys.SFTP_UPLOAD_PRESENT_BACKUP);
|
||||
this.uploadBackupFileName = configStore.string(ConfigKeys.SFTP_UPLOAD_BACKUP_FILE_NAME);
|
||||
}
|
||||
|
||||
public Integer getPreviewSize() {
|
||||
return previewSize.value;
|
||||
}
|
||||
|
||||
public Boolean getUploadPresentBackup() {
|
||||
return uploadPresentBackup.value;
|
||||
}
|
||||
|
||||
public String getUploadBackupFileName() {
|
||||
return uploadBackupFileName.value;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package cd.casic.module.terminal.define.operator;
|
||||
|
||||
import cd.casic.module.terminal.common.annotation.Module;
|
||||
import cd.casic.module.terminal.common.InitializingOperatorTypes;
|
||||
import cd.casic.module.terminal.common.OperatorType;
|
||||
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||
import java.util.List;
|
||||
import static cd.casic.module.terminal.common.OperatorRiskLevel.*;
|
||||
|
||||
|
||||
/**
|
||||
* 终端 操作日志类型
|
||||
*
|
||||
*/
|
||||
@Module("asset:terminal")
|
||||
public class TerminalOperatorType extends InitializingOperatorTypes {
|
||||
|
||||
public static final String CONNECT = "terminal:connect";
|
||||
|
||||
public static final String DELETE_SFTP_LOG = "terminal:delete-sftp-log";
|
||||
|
||||
public static final String SFTP_MKDIR = "terminal:sftp-mkdir";
|
||||
|
||||
public static final String SFTP_TOUCH = "terminal:sftp-touch";
|
||||
|
||||
public static final String SFTP_MOVE = "terminal:sftp-move";
|
||||
|
||||
public static final String SFTP_REMOVE = "terminal:sftp-remove";
|
||||
|
||||
public static final String SFTP_TRUNCATE = "terminal:sftp-truncate";
|
||||
|
||||
public static final String SFTP_CHMOD = "terminal:sftp-chmod";
|
||||
|
||||
public static final String SFTP_SET_CONTENT = "terminal:sftp-set-content";
|
||||
|
||||
public static final String SFTP_UPLOAD = "terminal:sftp-upload";
|
||||
|
||||
public static final String SFTP_DOWNLOAD = "terminal:sftp-download";
|
||||
|
||||
public static final List<String> SFTP_TYPES = Lists.of(
|
||||
SFTP_MKDIR,
|
||||
SFTP_TOUCH,
|
||||
SFTP_MOVE,
|
||||
SFTP_REMOVE,
|
||||
SFTP_TRUNCATE,
|
||||
SFTP_CHMOD,
|
||||
SFTP_SET_CONTENT,
|
||||
SFTP_UPLOAD,
|
||||
SFTP_DOWNLOAD
|
||||
);
|
||||
|
||||
@Override
|
||||
public OperatorType[] types() {
|
||||
return new OperatorType[]{
|
||||
new OperatorType(L, CONNECT, "连接主机 ${connectType} <sb>${hostName}</sb>"),
|
||||
new OperatorType(H, DELETE_SFTP_LOG, "删除 SFTP 操作日志 <sb>${count}</sb> 条"),
|
||||
new OperatorType(L, SFTP_MKDIR, "创建文件夹 ${hostName} <sb>${path}</sb>"),
|
||||
new OperatorType(L, SFTP_TOUCH, "创建文件 ${hostName} <sb>${path}</sb>"),
|
||||
new OperatorType(M, SFTP_MOVE, "移动文件 ${hostName} <sb>${path}</sb> 至 <sb>${target}</sb>"),
|
||||
new OperatorType(H, SFTP_REMOVE, "删除文件 ${hostName} <sb>${path}</sb>"),
|
||||
new OperatorType(H, SFTP_TRUNCATE, "截断文件 ${hostName} <sb>${path}</sb>"),
|
||||
new OperatorType(M, SFTP_CHMOD, "文件提权 ${hostName} <sb>${path}</sb> <sb>${mod}</sb>"),
|
||||
new OperatorType(M, SFTP_SET_CONTENT, "修改文件内容 ${hostName} <sb>${path}</sb>"),
|
||||
new OperatorType(M, SFTP_UPLOAD, "上传文件 ${hostName} <sb>${path}</sb>"),
|
||||
new OperatorType(M, SFTP_DOWNLOAD, "下载文件 ${hostName} <sb>${path}</sb>"),
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package cd.casic.module.terminal.enums;
|
||||
|
||||
import cd.casic.module.terminal.common.GenericsDataModel;
|
||||
import cd.casic.module.terminal.common.GenericsDataStrategy;
|
||||
import cd.casic.module.terminal.common.GenericsStrategyDefinition;
|
||||
import cd.casic.module.terminal.host.extra.strategy.HostLabelExtraStrategy;
|
||||
import cd.casic.module.terminal.host.extra.strategy.HostSpecExtraStrategy;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
/**
|
||||
* 主机额外配置项枚举
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/20 22:48
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum HostExtraItemEnum implements GenericsStrategyDefinition {
|
||||
|
||||
/**
|
||||
* SSH 额外配置
|
||||
*/
|
||||
// SSH(HostSshExtraStrategy.class, true),
|
||||
|
||||
/**
|
||||
* 标签额外配置
|
||||
*/
|
||||
LABEL(HostLabelExtraStrategy.class, true),
|
||||
|
||||
/**
|
||||
* 规格信息配置
|
||||
*/
|
||||
SPEC(HostSpecExtraStrategy.class, false),
|
||||
|
||||
;
|
||||
|
||||
private final Class<? extends GenericsDataStrategy<? extends GenericsDataModel>> strategyClass;
|
||||
|
||||
private final boolean userExtra;
|
||||
|
||||
public static HostExtraItemEnum of(String item) {
|
||||
if (item == null) {
|
||||
return null;
|
||||
}
|
||||
for (HostExtraItemEnum value : values()) {
|
||||
if (value.name().equals(item)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package cd.casic.module.terminal.enums;
|
||||
|
||||
/**
|
||||
* 主机认证类型
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/20 21:41
|
||||
*/
|
||||
public enum HostExtraSshAuthTypeEnum {
|
||||
|
||||
/**
|
||||
* 默认认证方式
|
||||
*/
|
||||
DEFAULT,
|
||||
|
||||
/**
|
||||
* 自定义密钥认证
|
||||
*/
|
||||
CUSTOM_KEY,
|
||||
|
||||
/**
|
||||
* 自定义身份认证
|
||||
*/
|
||||
CUSTOM_IDENTITY,
|
||||
|
||||
;
|
||||
|
||||
public static HostExtraSshAuthTypeEnum of(String type) {
|
||||
if (type == null) {
|
||||
return DEFAULT;
|
||||
}
|
||||
for (HostExtraSshAuthTypeEnum value : values()) {
|
||||
if (value.name().equals(type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package cd.casic.module.terminal.enums;
|
||||
|
||||
/**
|
||||
* 主机身份类型
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/9/21 19:01
|
||||
*/
|
||||
public enum HostIdentityTypeEnum {
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
PASSWORD,
|
||||
|
||||
/**
|
||||
* 密钥
|
||||
*/
|
||||
KEY,
|
||||
|
||||
;
|
||||
|
||||
public static HostIdentityTypeEnum of(String type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
for (HostIdentityTypeEnum value : values()) {
|
||||
if (value.name().equals(type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package cd.casic.module.terminal.enums;
|
||||
|
||||
/**
|
||||
* 主机认证类型 - ssh
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/9/21 19:01
|
||||
*/
|
||||
public enum HostSshAuthTypeEnum {
|
||||
|
||||
/**
|
||||
* 密码认证
|
||||
*/
|
||||
PASSWORD,
|
||||
|
||||
/**
|
||||
* 密钥认证
|
||||
*/
|
||||
KEY,
|
||||
|
||||
/**
|
||||
* 身份认证
|
||||
*/
|
||||
IDENTITY,
|
||||
|
||||
;
|
||||
|
||||
public static HostSshAuthTypeEnum of(String type) {
|
||||
if (type == null) {
|
||||
return PASSWORD;
|
||||
}
|
||||
for (HostSshAuthTypeEnum value : values()) {
|
||||
if (value.name().equals(type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return PASSWORD;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package cd.casic.module.terminal.enums;
|
||||
|
||||
/**
|
||||
* 主机状态
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/7/17 16:07
|
||||
*/
|
||||
public enum HostStatusEnum {
|
||||
|
||||
/**
|
||||
* 停用
|
||||
*/
|
||||
DISABLED,
|
||||
|
||||
/**
|
||||
* 启用
|
||||
*/
|
||||
ENABLED,
|
||||
|
||||
;
|
||||
|
||||
public static HostStatusEnum of(String name) {
|
||||
if (name == null) {
|
||||
return null;
|
||||
}
|
||||
for (HostStatusEnum value : values()) {
|
||||
if (value.name().equals(name)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package cd.casic.module.terminal.enums;
|
||||
|
||||
import cd.casic.module.terminal.common.GenericsDataModel;
|
||||
import cd.casic.module.terminal.common.GenericsDataStrategy;
|
||||
import cd.casic.module.terminal.common.GenericsStrategyDefinition;
|
||||
import cd.casic.module.terminal.host.config.strategy.HostSshConfigStrategy;
|
||||
import cn.orionsec.kit.lang.constant.Const;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 主机配置类型枚举
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/9/11 14:37
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum HostTypeEnum implements GenericsStrategyDefinition {
|
||||
|
||||
/**
|
||||
* SSH
|
||||
*/
|
||||
SSH(HostSshConfigStrategy.class),
|
||||
|
||||
;
|
||||
|
||||
private final Class<? extends GenericsDataStrategy<? extends GenericsDataModel>> strategyClass;
|
||||
|
||||
public static HostTypeEnum of(String type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
for (HostTypeEnum value : values()) {
|
||||
if (value.name().equals(type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package cd.casic.module.terminal.enums;
|
||||
|
||||
/**
|
||||
* 终端连接类型
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/26 22:27
|
||||
*/
|
||||
public enum TerminalConnectTypeEnum {
|
||||
|
||||
/**
|
||||
* ssh
|
||||
*/
|
||||
SSH,
|
||||
|
||||
/**
|
||||
* sftp
|
||||
*/
|
||||
SFTP,
|
||||
|
||||
;
|
||||
|
||||
public static TerminalConnectTypeEnum of(String type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
for (TerminalConnectTypeEnum value : values()) {
|
||||
if (value.name().equalsIgnoreCase(type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
package cd.casic.module.terminal.host.config.model;
|
||||
|
||||
import cd.casic.module.terminal.common.GenericsDataModel;
|
||||
import cd.casic.module.terminal.common.UpdatePasswordAction;
|
||||
import jakarta.validation.constraints.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
*
|
||||
* 主机 SSH 配置
|
||||
*
|
||||
* @author Yuru Pu
|
||||
* @version 1.0
|
||||
* @since 2025/7/15 15:22
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class HostSshConfigModel implements GenericsDataModel, UpdatePasswordAction {
|
||||
|
||||
/**
|
||||
* 主机端口
|
||||
*/
|
||||
@NotNull
|
||||
@Min(value = 1)
|
||||
@Max(value = 65535)
|
||||
private Integer port;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@Size(max = 128)
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 认证方式
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 12)
|
||||
private String authType;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 身份id
|
||||
*/
|
||||
private Long identityId;
|
||||
|
||||
/**
|
||||
* 密钥id
|
||||
*/
|
||||
private Long keyId;
|
||||
|
||||
/**
|
||||
* 连接超时时间
|
||||
*/
|
||||
@NotNull
|
||||
@Min(value = 1)
|
||||
@Max(value = 100000)
|
||||
private Integer connectTimeout;
|
||||
|
||||
/**
|
||||
* SSH输出编码
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 12)
|
||||
private String charset;
|
||||
|
||||
/**
|
||||
* 文件名称编码
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 12)
|
||||
private String fileNameCharset;
|
||||
|
||||
/**
|
||||
* 文件内容编码
|
||||
*/
|
||||
@NotBlank
|
||||
@Size(max = 12)
|
||||
private String fileContentCharset;
|
||||
|
||||
/**
|
||||
* 是否使用新密码 仅参数
|
||||
*/
|
||||
private Boolean useNewPassword;
|
||||
|
||||
/**
|
||||
* 是否已设置密码 仅返回
|
||||
*/
|
||||
private Boolean hasPassword;
|
||||
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
package cd.casic.module.terminal.host.config.strategy;
|
||||
|
||||
import cd.casic.module.machine.dal.mysql.MachineInfoMapper;
|
||||
import cd.casic.module.machine.dal.mysql.SecretKeyMapper;
|
||||
import cd.casic.module.terminal.common.AbstractGenericsDataStrategy;
|
||||
import cd.casic.module.terminal.common.AesEncryptUtils;
|
||||
import cd.casic.module.terminal.common.ErrorMessage;
|
||||
import cd.casic.module.terminal.enums.HostSshAuthTypeEnum;
|
||||
import cd.casic.module.terminal.host.config.model.HostSshConfigModel;
|
||||
import cd.casic.module.terminal.utils.RsaParamDecryptUtils;
|
||||
import cd.casic.module.terminal.utils.Valid;
|
||||
import cn.orionsec.kit.lang.constant.Const;
|
||||
import cn.orionsec.kit.lang.utils.Booleans;
|
||||
import cn.orionsec.kit.lang.utils.Charsets;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 主机 SSH 配置策略
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/9/19 14:26
|
||||
*/
|
||||
@Component
|
||||
public class HostSshConfigStrategy extends AbstractGenericsDataStrategy<HostSshConfigModel> {
|
||||
|
||||
@Autowired
|
||||
private SecretKeyMapper secretKeyMapper;
|
||||
|
||||
@Autowired
|
||||
private MachineInfoMapper machineInfoMapper;
|
||||
|
||||
private static final String USERNAME = "root";
|
||||
|
||||
public HostSshConfigStrategy() {
|
||||
super(HostSshConfigModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HostSshConfigModel getDefault() {
|
||||
return HostSshConfigModel.builder()
|
||||
.port(22)
|
||||
.username(USERNAME)
|
||||
.authType(HostSshAuthTypeEnum.PASSWORD.name())
|
||||
.connectTimeout(Const.MS_S_10)
|
||||
.charset(Const.UTF_8)
|
||||
.fileNameCharset(Const.UTF_8)
|
||||
.fileContentCharset(Const.UTF_8)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preValid(HostSshConfigModel model) {
|
||||
// 验证编码格式
|
||||
this.validCharset(model.getCharset());
|
||||
this.validCharset(model.getFileNameCharset());
|
||||
this.validCharset(model.getFileContentCharset());
|
||||
// 检查主机密钥是否存在
|
||||
Long keyId = model.getKeyId();
|
||||
if (keyId != null) {
|
||||
Valid.notNull(secretKeyMapper.selectById(keyId), ErrorMessage.KEY_ABSENT);
|
||||
}
|
||||
// 检查主机身份是否存在
|
||||
Long identityId = model.getIdentityId();
|
||||
if (identityId != null) {
|
||||
Valid.notNull(machineInfoMapper.selectById(identityId), ErrorMessage.IDENTITY_ABSENT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void valid(HostSshConfigModel model) {
|
||||
// 验证填充后的参数
|
||||
Valid.valid(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateFill(HostSshConfigModel beforeModel, HostSshConfigModel afterModel) {
|
||||
// 加密密码
|
||||
this.checkEncryptPassword(beforeModel, afterModel);
|
||||
afterModel.setHasPassword(null);
|
||||
afterModel.setUseNewPassword(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toView(HostSshConfigModel model) {
|
||||
if (model == null) {
|
||||
return;
|
||||
}
|
||||
model.setHasPassword(Strings.isNotBlank(model.getPassword()));
|
||||
model.setPassword(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查加密密码
|
||||
*
|
||||
* @param before before
|
||||
* @param after after
|
||||
*/
|
||||
private void checkEncryptPassword(HostSshConfigModel before, HostSshConfigModel after) {
|
||||
// 非密码认证/使用原始密码则直接赋值
|
||||
if (!HostSshAuthTypeEnum.PASSWORD.name().equals(after.getAuthType())
|
||||
|| !Booleans.isTrue(after.getUseNewPassword())) {
|
||||
if (before != null) {
|
||||
after.setPassword(before.getPassword());
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 检查新密码
|
||||
String newPassword = Valid.notBlank(after.getPassword(), ErrorMessage.PASSWORD_MISSING);
|
||||
// 解密密码
|
||||
newPassword = RsaParamDecryptUtils.decrypt(newPassword);
|
||||
Valid.notBlank(newPassword, ErrorMessage.DECRYPT_ERROR);
|
||||
// 设置密码
|
||||
after.setPassword(AesEncryptUtils.encryptAsString(newPassword));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查编码格式
|
||||
*
|
||||
* @param charset charset
|
||||
*/
|
||||
private void validCharset(String charset) {
|
||||
Valid.isTrue(Charsets.isSupported(charset), ErrorMessage.UNSUPPORTED_CHARSET, charset);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package cd.casic.module.terminal.host.extra.model;
|
||||
|
||||
import cd.casic.module.terminal.common.GenericsDataModel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 主机拓展信息 - 标签模型
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/29 23:16
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class HostLabelExtraModel implements GenericsDataModel {
|
||||
|
||||
/**
|
||||
* 别名
|
||||
*/
|
||||
private String alias;
|
||||
|
||||
/**
|
||||
* 颜色
|
||||
*/
|
||||
private String color;
|
||||
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
package cd.casic.module.terminal.host.extra.model;
|
||||
|
||||
import cd.casic.module.terminal.common.GenericsDataModel;
|
||||
import lombok.*;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 主机拓展信息 - 规格模型
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/3/24 0:34
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class HostSpecExtraModel implements GenericsDataModel {
|
||||
|
||||
/**
|
||||
* sn
|
||||
*/
|
||||
private String sn;
|
||||
|
||||
/**
|
||||
* 系统名称
|
||||
*/
|
||||
private String osName;
|
||||
|
||||
/**
|
||||
* cpu 核心数
|
||||
*/
|
||||
private Integer cpuCore;
|
||||
|
||||
/**
|
||||
* cpu 频率
|
||||
*/
|
||||
private Double cpuFrequency;
|
||||
|
||||
/**
|
||||
* cpu 型号
|
||||
*/
|
||||
private String cpuModel;
|
||||
|
||||
/**
|
||||
* 内存大小
|
||||
*/
|
||||
private Double memorySize;
|
||||
|
||||
/**
|
||||
* 硬盘大小
|
||||
*/
|
||||
private Double diskSize;
|
||||
|
||||
/**
|
||||
* 上行带宽
|
||||
*/
|
||||
private Integer inBandwidth;
|
||||
|
||||
/**
|
||||
* 下行带宽
|
||||
*/
|
||||
private Integer outBandwidth;
|
||||
|
||||
/**
|
||||
* 公网 ip 列表
|
||||
*/
|
||||
private List<String> publicIpAddress;
|
||||
|
||||
/**
|
||||
* 内网 ip 列表
|
||||
*/
|
||||
private List<String> privateIpAddress;
|
||||
|
||||
/**
|
||||
* 负责人
|
||||
*/
|
||||
private String chargePerson;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createdTime;
|
||||
|
||||
/**
|
||||
* 到期时间
|
||||
*/
|
||||
private Date expiredTime;
|
||||
|
||||
/**
|
||||
* 扩展信息
|
||||
*/
|
||||
@Singular
|
||||
private List<HostSpecExtraItem> items;
|
||||
|
||||
/**
|
||||
* 扩展信息项
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class HostSpecExtraItem {
|
||||
|
||||
/**
|
||||
* 标签
|
||||
*/
|
||||
private String label;
|
||||
|
||||
/**
|
||||
* 值
|
||||
*/
|
||||
private String value;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package cd.casic.module.terminal.host.extra.model;
|
||||
|
||||
import cd.casic.module.terminal.common.GenericsDataModel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 主机拓展信息 - ssh 模型
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/20 21:36
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class HostSshExtraModel implements GenericsDataModel {
|
||||
|
||||
/**
|
||||
* 认证方式
|
||||
*/
|
||||
private String authType;
|
||||
|
||||
/**
|
||||
* 认证方式
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 主机密钥
|
||||
*/
|
||||
private Long keyId;
|
||||
|
||||
/**
|
||||
* 主机身份
|
||||
*/
|
||||
private Long identityId;
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package cd.casic.module.terminal.host.extra.strategy;
|
||||
|
||||
import cd.casic.module.terminal.common.AbstractGenericsDataStrategy;
|
||||
import cd.casic.module.terminal.host.extra.model.HostLabelExtraModel;
|
||||
import cn.orionsec.kit.lang.constant.Const;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 主机拓展信息 - 标签模型处理策略
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/29 23:16
|
||||
*/
|
||||
@Component
|
||||
public class HostLabelExtraStrategy extends AbstractGenericsDataStrategy<HostLabelExtraModel> {
|
||||
|
||||
public HostLabelExtraStrategy() {
|
||||
super(HostLabelExtraModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HostLabelExtraModel getDefault() {
|
||||
return HostLabelExtraModel.builder()
|
||||
// 透明
|
||||
.color(Const.EMPTY)
|
||||
// 无别名
|
||||
.alias(Const.EMPTY)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFill(HostLabelExtraModel beforeModel, HostLabelExtraModel afterModel) {
|
||||
// 为空则覆盖
|
||||
if (afterModel.getAlias() == null) {
|
||||
afterModel.setAlias(beforeModel.getAlias());
|
||||
}
|
||||
if (afterModel.getColor() == null) {
|
||||
afterModel.setColor(beforeModel.getColor());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package cd.casic.module.terminal.host.extra.strategy;
|
||||
|
||||
|
||||
import cd.casic.module.terminal.common.AbstractGenericsDataStrategy;
|
||||
import cd.casic.module.terminal.host.extra.model.HostSpecExtraModel;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 主机规格额外信息策略
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2025/3/24 0:21
|
||||
*/
|
||||
@Component
|
||||
public class HostSpecExtraStrategy extends AbstractGenericsDataStrategy<HostSpecExtraModel> {
|
||||
|
||||
public HostSpecExtraStrategy() {
|
||||
super(HostSpecExtraModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HostSpecExtraModel getDefault() {
|
||||
return new HostSpecExtraModel();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package cd.casic.module.terminal.host.jsch;
|
||||
|
||||
/**
|
||||
* 连接消息
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/7/11 16:30
|
||||
*/
|
||||
public interface SessionMessage {
|
||||
|
||||
String AUTHENTICATION_FAILURE = "身份认证失败. {}";
|
||||
|
||||
String SERVER_UNREACHABLE = "无法连接至服务器. {}";
|
||||
|
||||
String CONNECTION_TIMEOUT = "连接服务器超时. {}";
|
||||
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
package cd.casic.module.terminal.host.jsch;
|
||||
|
||||
|
||||
import cd.casic.module.terminal.common.AesEncryptUtils;
|
||||
import cd.casic.module.terminal.common.AppConst;
|
||||
import cd.casic.module.terminal.controller.dto.TerminalConnectDTO;
|
||||
import cn.orionsec.kit.lang.constant.Const;
|
||||
import cn.orionsec.kit.lang.exception.AuthenticationException;
|
||||
import cn.orionsec.kit.lang.exception.argument.InvalidArgumentException;
|
||||
import cn.orionsec.kit.lang.utils.Exceptions;
|
||||
import cn.orionsec.kit.lang.utils.Strings;
|
||||
import cn.orionsec.kit.net.host.SessionHolder;
|
||||
import cn.orionsec.kit.net.host.SessionStore;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* sessionStore 工具类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/7/11 16:58
|
||||
*/
|
||||
@Slf4j
|
||||
public class SessionStores {
|
||||
|
||||
protected static final ThreadLocal<String> CURRENT_ADDRESS = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 打开 sessionStore
|
||||
*
|
||||
* @param conn conn
|
||||
* @return sessionStore
|
||||
*/
|
||||
public static SessionStore openSessionStore(TerminalConnectDTO conn) {
|
||||
Long hostId = conn.getHostId();
|
||||
String address = conn.getHostAddress();
|
||||
String username = conn.getUsername();
|
||||
log.info("SessionStores-open-start hostId: {}, address: {}, username: {}", hostId, address, username);
|
||||
try {
|
||||
CURRENT_ADDRESS.set(address);
|
||||
// 创建会话
|
||||
SessionHolder sessionHolder = SessionHolder.create();
|
||||
// sessionHolder.setLogger(SessionLogger.INFO);
|
||||
SessionStore session = createSessionStore(conn, sessionHolder);
|
||||
// 设置版本
|
||||
session.getSession().setClientVersion("SSH-2.0-ORION_VISOR_V" + AppConst.VERSION);
|
||||
// 连接
|
||||
session.connect();
|
||||
log.info("SessionStores-open-success hostId: {}, address: {}, username: {}", hostId, address, username);
|
||||
return session;
|
||||
} catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
log.error("SessionStores-open-error hostId: {}, address: {}, username: {}, message: {}", hostId, address, username, message, e);
|
||||
throw Exceptions.app(getErrorMessage(e), e);
|
||||
} finally {
|
||||
CURRENT_ADDRESS.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 sessionStore
|
||||
*
|
||||
* @param conn conn
|
||||
* @param sessionHolder sessionHolder
|
||||
* @return sessionStore
|
||||
*/
|
||||
private static SessionStore createSessionStore(TerminalConnectDTO conn, SessionHolder sessionHolder) {
|
||||
final boolean useKey = conn.getKeyId() != null;
|
||||
// 使用密钥认证
|
||||
if (useKey) {
|
||||
// 加载密钥
|
||||
String publicKey = Optional.ofNullable(conn.getPublicKey())
|
||||
.map(AesEncryptUtils::decryptAsString)
|
||||
.orElse(null);
|
||||
String privateKey = Optional.ofNullable(conn.getPrivateKey())
|
||||
.map(AesEncryptUtils::decryptAsString)
|
||||
.orElse(null);
|
||||
String password = Optional.ofNullable(conn.getPrivateKeyPassword())
|
||||
.map(AesEncryptUtils::decryptAsString)
|
||||
.orElse(null);
|
||||
sessionHolder.addIdentityValue(String.valueOf(conn.getKeyId()),
|
||||
privateKey,
|
||||
publicKey,
|
||||
password);
|
||||
}
|
||||
// 获取会话
|
||||
SessionStore session = sessionHolder.getSession(conn.getHostAddress(), conn.getHostPort(), conn.getUsername());
|
||||
// 使用密码认证
|
||||
if (!useKey) {
|
||||
String password = conn.getPassword();
|
||||
if (!Strings.isEmpty(password)) {
|
||||
// session.password(AesEncryptUtils.decryptAsString(password));
|
||||
session.password(password);
|
||||
}
|
||||
}
|
||||
// 超时时间
|
||||
session.timeout(conn.getTimeout());
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
*
|
||||
* @param e e
|
||||
* @return errorMessage
|
||||
*/
|
||||
private static String getErrorMessage(Exception e) {
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
String host = CURRENT_ADDRESS.get();
|
||||
String message = e.getMessage();
|
||||
if (Strings.contains(message, Const.TIMEOUT)) {
|
||||
// 连接超时
|
||||
return Strings.format(SessionMessage.CONNECTION_TIMEOUT, host);
|
||||
} else if (Exceptions.isCausedBy(e, AuthenticationException.class)) {
|
||||
// 认证失败
|
||||
return Strings.format(SessionMessage.AUTHENTICATION_FAILURE, host);
|
||||
} else if (Exceptions.isCausedBy(e, InvalidArgumentException.class)) {
|
||||
// 参数错误
|
||||
if (Strings.isBlank(message)) {
|
||||
return Strings.format(SessionMessage.SERVER_UNREACHABLE, host);
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
} else {
|
||||
// 其他错误
|
||||
return Strings.format(SessionMessage.SERVER_UNREACHABLE, host);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package cd.casic.module.terminal.host.terminal.constant;
|
||||
|
||||
/**
|
||||
* 终端信息常量
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/1/3 16:57
|
||||
*/
|
||||
public interface TerminalMessage {
|
||||
|
||||
String CONFIG_DISABLED = "SSH configuration has been disabled.";
|
||||
|
||||
String AUTHENTICATION_FAILURE = "authentication failed. please check the configuration.";
|
||||
|
||||
String SERVER_UNREACHABLE = "remote server unreachable. please check the configuration.";
|
||||
|
||||
String CONNECTION_FAILED = "connection failed.";
|
||||
|
||||
String CONNECTION_TIMEOUT = "connection timeout.";
|
||||
|
||||
String CONNECTION_CLOSED = "connection closed.";
|
||||
|
||||
String FORCED_OFFLINE = "forced offline.";
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package cd.casic.module.terminal.host.terminal.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* sftp 获取文件内容缓存对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/10/10 20:49
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "SftpGetContentCacheDTO", description = "sftp 获取文件内容缓存对象")
|
||||
public class SftpGetContentCacheDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "主机id")
|
||||
private Long hostId;
|
||||
|
||||
@Schema(description = "文件路径")
|
||||
private String path;
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package cd.casic.module.terminal.host.terminal.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* sftp 获取文件内容缓存对象
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/10/10 20:49
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "SftpSetContentCacheDTO", description = "sftp 设置文件内容缓存对象")
|
||||
public class SftpSetContentCacheDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "主机id")
|
||||
private Long hostId;
|
||||
|
||||
@Schema(description = "文件路径")
|
||||
private String path;
|
||||
|
||||
}
|
@ -0,0 +1,260 @@
|
||||
package cd.casic.module.terminal.host.terminal.enums;
|
||||
|
||||
import cd.casic.module.terminal.common.holder.SpringHolder;
|
||||
import cd.casic.module.terminal.host.terminal.handler.*;
|
||||
import cd.casic.module.terminal.host.terminal.model.TerminalBasePayload;
|
||||
import cd.casic.module.terminal.host.terminal.model.request.*;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.Getter;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
/**
|
||||
* 输入操作类型枚举
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/29 15:33
|
||||
*/
|
||||
public enum InputTypeEnum {
|
||||
|
||||
/**
|
||||
* 终端连接检查
|
||||
*/
|
||||
CHECK("ck",
|
||||
TerminalCheckHandler.class,
|
||||
new String[]{"type", "sessionId", "hostId", "connectType"},
|
||||
TerminalCheckRequest.class,
|
||||
true),
|
||||
|
||||
|
||||
/**
|
||||
* 连接主机
|
||||
*/
|
||||
CONNECT("co",
|
||||
TerminalConnectHandler.class,
|
||||
new String[]{"type", "sessionId", "terminalType", "cols", "rows"},
|
||||
TerminalConnectRequest.class,
|
||||
true),
|
||||
|
||||
/**
|
||||
* 关闭连接
|
||||
*/
|
||||
CLOSE("cl",
|
||||
TerminalCloseHandler.class,
|
||||
new String[]{"type", "sessionId"},
|
||||
TerminalBasePayload.class,
|
||||
true),
|
||||
|
||||
/**
|
||||
* ping
|
||||
*/
|
||||
PING("p",
|
||||
TerminalPingHandler.class,
|
||||
new String[]{"type"},
|
||||
TerminalBasePayload.class,
|
||||
true),
|
||||
|
||||
/**
|
||||
* SSH 修改大小
|
||||
*/
|
||||
SSH_RESIZE("rs",
|
||||
SshResizeHandler.class,
|
||||
new String[]{"type", "sessionId", "cols", "rows"},
|
||||
SshResizeRequest.class,
|
||||
true),
|
||||
|
||||
/**
|
||||
* SSH 输入
|
||||
*/
|
||||
SSH_INPUT("i",
|
||||
SshInputHandler.class,
|
||||
new String[]{"type", "sessionId", "command"},
|
||||
SshInputRequest.class,
|
||||
false),
|
||||
|
||||
/**
|
||||
* SFTP 文件列表
|
||||
*/
|
||||
SFTP_LIST("ls",
|
||||
SftpListHandler.class,
|
||||
new String[]{"type", "sessionId", "showHiddenFile", "path"},
|
||||
SftpListRequest.class,
|
||||
true),
|
||||
|
||||
/**
|
||||
* SFTP 创建文件夹
|
||||
*/
|
||||
SFTP_MKDIR("mk",
|
||||
SftpMakeDirectoryHandler.class,
|
||||
new String[]{"type", "sessionId", "path"},
|
||||
SftpBaseRequest.class,
|
||||
true),
|
||||
|
||||
/**
|
||||
* SFTP 创建文件
|
||||
*/
|
||||
SFTP_TOUCH("to",
|
||||
SftpTouchHandler.class,
|
||||
new String[]{"type", "sessionId", "path"},
|
||||
SftpBaseRequest.class,
|
||||
true),
|
||||
|
||||
/**
|
||||
* SFTP 移动文件
|
||||
*/
|
||||
SFTP_MOVE("mv",
|
||||
SftpMoveHandler.class,
|
||||
new String[]{"type", "sessionId", "path", "target"},
|
||||
SftpMoveRequest.class,
|
||||
true),
|
||||
|
||||
/**
|
||||
* SFTP 删除文件
|
||||
*/
|
||||
SFTP_REMOVE("rm",
|
||||
SftpRemoveHandler.class,
|
||||
new String[]{"type", "sessionId", "path"},
|
||||
SftpBaseRequest.class,
|
||||
true),
|
||||
|
||||
/**
|
||||
* SFTP 截断文件
|
||||
*/
|
||||
SFTP_TRUNCATE("tc",
|
||||
SftpTruncateHandler.class,
|
||||
new String[]{"type", "sessionId", "path"},
|
||||
SftpBaseRequest.class,
|
||||
true),
|
||||
|
||||
/**
|
||||
* SFTP 修改文件权限
|
||||
*/
|
||||
SFTP_CHMOD("cm",
|
||||
SftpChangeModeHandler.class,
|
||||
new String[]{"type", "sessionId", "path", "mod"},
|
||||
SftpChangeModeRequest.class,
|
||||
true),
|
||||
|
||||
/**
|
||||
* SFTP 下载文件夹展开文件
|
||||
*/
|
||||
SFTP_DOWNLOAD_FLAT_DIRECTORY("df",
|
||||
SftpDownloadFlatDirectoryHandler.class,
|
||||
new String[]{"type", "sessionId", "currentPath", "path"},
|
||||
SftpDownloadFlatDirectoryRequest.class,
|
||||
true),
|
||||
|
||||
/**
|
||||
* SFTP 获取内容
|
||||
*/
|
||||
SFTP_GET_CONTENT("gc",
|
||||
SftpGetContentHandler.class,
|
||||
new String[]{"type", "sessionId", "path"},
|
||||
SftpBaseRequest.class,
|
||||
true),
|
||||
|
||||
/**
|
||||
* SFTP 修改内容
|
||||
*/
|
||||
SFTP_SET_CONTENT("sc",
|
||||
SftpSetContentHandler.class,
|
||||
new String[]{"type", "sessionId", "path"},
|
||||
SftpBaseRequest.class,
|
||||
true),
|
||||
;
|
||||
|
||||
private static final char SEPARATOR = '|';
|
||||
|
||||
@Getter
|
||||
private final String type;
|
||||
|
||||
private final Class<? extends ITerminalHandler<? extends TerminalBasePayload>> handlerBean;
|
||||
|
||||
private final String[] payloadDefine;
|
||||
|
||||
private final Class<? extends TerminalBasePayload> payloadClass;
|
||||
|
||||
@Getter
|
||||
private final boolean asyncExec;
|
||||
|
||||
@Getter
|
||||
private ITerminalHandler<? extends TerminalBasePayload> handler;
|
||||
|
||||
|
||||
<T extends TerminalBasePayload> InputTypeEnum(String type,
|
||||
Class<? extends ITerminalHandler<T>> handlerBean,
|
||||
String[] payloadDefine,
|
||||
Class<T> payloadClass,
|
||||
boolean asyncExec) {
|
||||
this.type = type;
|
||||
this.handlerBean = handlerBean;
|
||||
this.payloadDefine = payloadDefine;
|
||||
this.payloadClass = payloadClass;
|
||||
this.asyncExec = asyncExec;
|
||||
}
|
||||
|
||||
public static InputTypeEnum of(String payload) {
|
||||
if (payload == null) {
|
||||
return null;
|
||||
}
|
||||
for (InputTypeEnum value : values()) {
|
||||
if (payload.startsWith(value.type + SEPARATOR) || payload.equals(value.type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析请求
|
||||
*
|
||||
* @param payload payload
|
||||
* @param <T> T
|
||||
* @return payload
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends TerminalBasePayload> T parse(String payload) {
|
||||
JSONObject object = new JSONObject();
|
||||
int curr = 0;
|
||||
int len = payload.length();
|
||||
for (int i = 0, pl = payloadDefine.length; i < pl; i++) {
|
||||
if (i == pl - 1) {
|
||||
// 最后一次
|
||||
object.put(payloadDefine[i], payload.substring(curr, len));
|
||||
} else {
|
||||
// 非最后一次
|
||||
StringBuilder tmp = new StringBuilder();
|
||||
for (; curr < len; curr++) {
|
||||
char c = payload.charAt(curr);
|
||||
if (c == SEPARATOR) {
|
||||
object.put(payloadDefine[i], tmp.toString());
|
||||
curr++;
|
||||
break;
|
||||
} else {
|
||||
tmp.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (T) object.toJavaObject(payloadClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型字段定义
|
||||
*/
|
||||
@Component
|
||||
static class TypeFieldDefinition {
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
for (InputTypeEnum value : InputTypeEnum.values()) {
|
||||
value.handler = SpringHolder.getBean(value.handlerBean);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
package cd.casic.module.terminal.host.terminal.enums;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.json.matcher.ReplacementFormatters;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 输出操作类型枚举
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/29 15:33
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum OutputTypeEnum {
|
||||
|
||||
/**
|
||||
* 终端连接检查
|
||||
*/
|
||||
CHECK("ck", "${type}|${sessionId}|${result}|${msg}"),
|
||||
|
||||
/**
|
||||
* 终端连接
|
||||
*/
|
||||
CONNECT("co", "${type}|${sessionId}|${result}|${msg}"),
|
||||
|
||||
/**
|
||||
* 关闭连接
|
||||
*/
|
||||
CLOSE("cl", "${type}|${sessionId}|${forceClose}|${msg}"),
|
||||
|
||||
/**
|
||||
* pong
|
||||
*/
|
||||
PONG("p", "${type}"),
|
||||
|
||||
/**
|
||||
* SSH 输出
|
||||
*/
|
||||
SSH_OUTPUT("o", "${type}|${sessionId}|${body}"),
|
||||
|
||||
/**
|
||||
* SFTP 文件列表
|
||||
*/
|
||||
SFTP_LIST("ls", "${type}|${sessionId}|${path}|${result}|${msg}|${body}"),
|
||||
|
||||
/**
|
||||
* SFTP 创建文件夹
|
||||
*/
|
||||
SFTP_MKDIR("mk", "${type}|${sessionId}|${result}|${msg}"),
|
||||
|
||||
/**
|
||||
* SFTP 创建文件
|
||||
*/
|
||||
SFTP_TOUCH("to", "${type}|${sessionId}|${result}|${msg}"),
|
||||
|
||||
/**
|
||||
* SFTP 移动文件
|
||||
*/
|
||||
SFTP_MOVE("mv", "${type}|${sessionId}|${result}|${msg}"),
|
||||
|
||||
/**
|
||||
* SFTP 删除文件
|
||||
*/
|
||||
SFTP_REMOVE("rm", "${type}|${sessionId}|${result}|${msg}"),
|
||||
|
||||
/**
|
||||
* SFTP 截断文件
|
||||
*/
|
||||
SFTP_TRUNCATE("tc", "${type}|${sessionId}|${result}|${msg}"),
|
||||
|
||||
/**
|
||||
* SFTP 修改文件权限
|
||||
*/
|
||||
SFTP_CHMOD("cm", "${type}|${sessionId}|${result}|${msg}"),
|
||||
|
||||
/**
|
||||
* SFTP 下载文件夹展开文件
|
||||
*/
|
||||
SFTP_DOWNLOAD_FLAT_DIRECTORY("df", "${type}|${sessionId}|${currentPath}|${result}|${msg}|${body}"),
|
||||
|
||||
/**
|
||||
* SFTP 获取文件内容
|
||||
*/
|
||||
SFTP_GET_CONTENT("gc", "${type}|${sessionId}|${result}|${msg}|${token}"),
|
||||
|
||||
/**
|
||||
* SFTP 修改文件内容
|
||||
*/
|
||||
SFTP_SET_CONTENT("sc", "${type}|${sessionId}|${result}|${msg}|${token}"),
|
||||
|
||||
;
|
||||
|
||||
private final String type;
|
||||
|
||||
private final String template;
|
||||
|
||||
/**
|
||||
* 格式化
|
||||
*
|
||||
* @param o o
|
||||
* @return 格式化
|
||||
*/
|
||||
public String format(Object o) {
|
||||
return ReplacementFormatters.format(this.template, o);
|
||||
}
|
||||
|
||||
public static OutputTypeEnum of(String type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
for (OutputTypeEnum value : values()) {
|
||||
if (value.type.equals(type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package cd.casic.module.terminal.host.terminal.enums;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 终端连接状态
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/26 22:27
|
||||
*/
|
||||
public enum TerminalConnectStatusEnum {
|
||||
|
||||
/**
|
||||
* 连接中
|
||||
*/
|
||||
CONNECTING,
|
||||
|
||||
/**
|
||||
* 完成
|
||||
*/
|
||||
COMPLETE,
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
FAILED,
|
||||
|
||||
/**
|
||||
* 强制下线
|
||||
*/
|
||||
FORCE_OFFLINE,
|
||||
|
||||
;
|
||||
|
||||
public static final List<String> FINISH_STATUS_LIST = Lists.of(COMPLETE.name(), FAILED.name(), FORCE_OFFLINE.name());
|
||||
|
||||
public static TerminalConnectStatusEnum of(String type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
for (TerminalConnectStatusEnum value : values()) {
|
||||
if (value.name().equals(type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package cd.casic.module.terminal.host.terminal.handler;
|
||||
|
||||
|
||||
import cd.casic.module.terminal.common.ErrorMessage;
|
||||
import cd.casic.module.terminal.common.OperatorLogs;
|
||||
import cd.casic.module.terminal.common.WebSockets;
|
||||
import cd.casic.module.terminal.host.terminal.enums.OutputTypeEnum;
|
||||
import cd.casic.module.terminal.host.terminal.manager.TerminalManager;
|
||||
import cd.casic.module.terminal.host.terminal.model.TerminalBasePayload;
|
||||
import cd.casic.module.terminal.host.terminal.model.TerminalConfig;
|
||||
import cd.casic.module.terminal.host.terminal.session.ITerminalSession;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 终端消息处理器 基类
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/29 18:59
|
||||
*/
|
||||
public abstract class AbstractTerminalHandler<T extends TerminalBasePayload> implements ITerminalHandler<T> {
|
||||
|
||||
@Resource
|
||||
protected TerminalManager terminalManager;
|
||||
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param channel channel
|
||||
* @param type type
|
||||
* @param body body
|
||||
* @param <E> E
|
||||
*/
|
||||
public <E extends TerminalBasePayload> void send(WebSocketSession channel, OutputTypeEnum type, E body) {
|
||||
body.setType(type.getType());
|
||||
// 发送消息
|
||||
this.send(channel, type.format(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param channel channel
|
||||
* @param message message
|
||||
*/
|
||||
protected void send(WebSocketSession channel, String message) {
|
||||
WebSockets.sendText(channel, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存操作日志
|
||||
*
|
||||
* @param payload payload
|
||||
* @param channel channel
|
||||
* @param extra extra
|
||||
* @param type type
|
||||
* @param startTime startTime
|
||||
* @param ex ex
|
||||
*/
|
||||
protected void saveOperatorLog(T payload,
|
||||
WebSocketSession channel,
|
||||
Map<String, Object> extra,
|
||||
String type,
|
||||
long startTime,
|
||||
Exception ex) {
|
||||
String channelId = channel.getId();
|
||||
String sessionId = payload.getSessionId();
|
||||
// 获取会话并且设置参数
|
||||
ITerminalSession session = terminalManager.getSession(channelId, sessionId);
|
||||
if (session != null) {
|
||||
TerminalConfig config = session.getConfig();
|
||||
extra.put(OperatorLogs.HOST_ID, config.getHostId());
|
||||
extra.put(OperatorLogs.HOST_NAME, config.getHostName());
|
||||
extra.put(OperatorLogs.ADDRESS, config.getAddress());
|
||||
}
|
||||
extra.put(OperatorLogs.CHANNEL_ID, channelId);
|
||||
extra.put(OperatorLogs.SESSION_ID, sessionId);
|
||||
// 获取日志
|
||||
// OperatorLogModel model = TerminalUtils.getOperatorLogModel(channel, extra, type, startTime, ex);
|
||||
// 保存
|
||||
// operatorLogFrameworkService.insert(model);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取错误信息
|
||||
*
|
||||
* @param ex ex
|
||||
* @return msg
|
||||
*/
|
||||
protected String getErrorMessage(Exception ex) {
|
||||
// 获取错误信息
|
||||
return ErrorMessage.getErrorMessage(ex, ErrorMessage.OPERATE_ERROR);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package cd.casic.module.terminal.host.terminal.handler;
|
||||
|
||||
import cd.casic.module.terminal.host.terminal.model.TerminalBasePayload;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
/**
|
||||
* 终端消息处理器
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/29 18:53
|
||||
*/
|
||||
public interface ITerminalHandler<T extends TerminalBasePayload> {
|
||||
|
||||
/**
|
||||
* 处理消息
|
||||
*
|
||||
* @param channel channel
|
||||
* @param payload payload
|
||||
*/
|
||||
void handle(WebSocketSession channel, T payload);
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package cd.casic.module.terminal.host.terminal.handler;
|
||||
|
||||
import cd.casic.module.terminal.common.BooleanBit;
|
||||
import cd.casic.module.terminal.common.OperatorLogs;
|
||||
import cd.casic.module.terminal.define.operator.TerminalOperatorType;
|
||||
import cd.casic.module.terminal.host.terminal.enums.OutputTypeEnum;
|
||||
import cd.casic.module.terminal.host.terminal.model.request.SftpChangeModeRequest;
|
||||
import cd.casic.module.terminal.host.terminal.model.response.SftpBaseResponse;
|
||||
import cd.casic.module.terminal.host.terminal.session.ISftpSession;
|
||||
|
||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* sftp 修改文件权限
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/19 11:13
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SftpChangeModeHandler extends AbstractTerminalHandler<SftpChangeModeRequest> {
|
||||
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, SftpChangeModeRequest payload) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
Integer mod = payload.getMod();
|
||||
log.info("SftpChangeModeHandler-handle start sessionId: {}, path: {}, mod: {}", sessionId, path, mod);
|
||||
Exception ex = null;
|
||||
// 修改权限
|
||||
try {
|
||||
session.chmod(path, mod);
|
||||
log.info("SftpChangeModeHandler-handle success sessionId: {}, path: {}, mod: {}", sessionId, path, mod);
|
||||
} catch (Exception e) {
|
||||
log.error("SftpChangeModeHandler-handle error sessionId: {}", sessionId, e);
|
||||
ex = e;
|
||||
}
|
||||
// 返回
|
||||
this.send(channel,
|
||||
OutputTypeEnum.SFTP_CHMOD,
|
||||
SftpBaseResponse.builder()
|
||||
.sessionId(sessionId)
|
||||
.result(BooleanBit.of(ex == null).getValue())
|
||||
.msg(this.getErrorMessage(ex))
|
||||
.build());
|
||||
// 保存操作日志
|
||||
Map<String, Object> extra = Maps.newMap();
|
||||
extra.put(OperatorLogs.PATH, path);
|
||||
extra.put(OperatorLogs.MOD, mod);
|
||||
this.saveOperatorLog(payload, channel,
|
||||
extra, TerminalOperatorType.SFTP_CHMOD,
|
||||
startTime, ex);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package cd.casic.module.terminal.host.terminal.handler;
|
||||
|
||||
import cd.casic.module.terminal.common.BooleanBit;
|
||||
import cd.casic.module.terminal.host.terminal.enums.OutputTypeEnum;
|
||||
import cd.casic.module.terminal.host.terminal.model.request.SftpDownloadFlatDirectoryRequest;
|
||||
import cd.casic.module.terminal.host.terminal.model.response.SftpDownloadFlatDirectoryResponse;
|
||||
import cd.casic.module.terminal.host.terminal.model.response.SftpFileVO;
|
||||
import cd.casic.module.terminal.host.terminal.session.ISftpSession;
|
||||
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* sftp 下载文件夹展开文件
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/19 11:13
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SftpDownloadFlatDirectoryHandler extends AbstractTerminalHandler<SftpDownloadFlatDirectoryRequest> {
|
||||
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, SftpDownloadFlatDirectoryRequest payload) {
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
String[] paths = payload.getPath().split("\\|");
|
||||
log.info("SftpDownloadFlatDirectoryHandler-handle start sessionId: {}, paths: {}", sessionId, Arrays.toString(paths));
|
||||
Exception ex = null;
|
||||
List<SftpFileVO> files = Lists.empty();
|
||||
// 展开文件夹内的全部文件
|
||||
try {
|
||||
files = session.flatDirectory(paths);
|
||||
log.info("SftpDownloadFlatDirectoryHandler-handle success sessionId: {}, paths: {}", sessionId, Arrays.toString(paths));
|
||||
} catch (Exception e) {
|
||||
log.error("SftpDownloadFlatDirectoryHandler-handle error sessionId: {}", sessionId, e);
|
||||
ex = e;
|
||||
}
|
||||
// 返回
|
||||
this.send(channel,
|
||||
OutputTypeEnum.SFTP_DOWNLOAD_FLAT_DIRECTORY,
|
||||
SftpDownloadFlatDirectoryResponse.builder()
|
||||
.sessionId(sessionId)
|
||||
.currentPath(payload.getCurrentPath())
|
||||
.body(JSON.toJSONString(files))
|
||||
.result(BooleanBit.of(ex == null).getValue())
|
||||
.msg(this.getErrorMessage(ex))
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package cd.casic.module.terminal.host.terminal.handler;
|
||||
|
||||
import cd.casic.module.terminal.common.BooleanBit;
|
||||
import cd.casic.module.terminal.dal.redis.TerminalRedisDAO;
|
||||
import cd.casic.module.terminal.define.cache.TerminalCacheKeyDefine;
|
||||
import cd.casic.module.terminal.host.terminal.dto.SftpGetContentCacheDTO;
|
||||
import cd.casic.module.terminal.host.terminal.enums.OutputTypeEnum;
|
||||
import cd.casic.module.terminal.host.terminal.model.request.SftpBaseRequest;
|
||||
import cd.casic.module.terminal.host.terminal.model.response.SftpGetContentResponse;
|
||||
import cd.casic.module.terminal.host.terminal.session.ISftpSession;
|
||||
import cn.orionsec.kit.lang.id.UUIds;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
/**
|
||||
* sftp 获取文件内容
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/19 11:13
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SftpGetContentHandler extends AbstractTerminalHandler<SftpBaseRequest> {
|
||||
|
||||
@Resource
|
||||
private TerminalRedisDAO terminalRedisDAO;
|
||||
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, SftpBaseRequest payload) {
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
log.info("SftpGetContentHandler-handle start sessionId: {}, path: {}", sessionId, path);
|
||||
String token = UUIds.random32();
|
||||
Exception ex = null;
|
||||
try {
|
||||
// 检查文件是否可编辑
|
||||
session.checkCanEdit(path);
|
||||
// 设置缓存
|
||||
String key = TerminalCacheKeyDefine.TERMINAL_SFTP_GET_CONTENT.format(token);
|
||||
SftpGetContentCacheDTO cache = SftpGetContentCacheDTO.builder()
|
||||
.hostId(session.getConfig().getHostId())
|
||||
.path(path)
|
||||
.build();
|
||||
terminalRedisDAO.setJson(key, TerminalCacheKeyDefine.TERMINAL_SFTP_GET_CONTENT, cache);
|
||||
log.info("SftpGetContentHandler-handle success sessionId: {}, path: {}", sessionId, path);
|
||||
} catch (Exception e) {
|
||||
log.error("SftpGetContentHandler-handle error sessionId: {}", sessionId, e);
|
||||
ex = e;
|
||||
}
|
||||
// 返回
|
||||
this.send(channel,
|
||||
OutputTypeEnum.SFTP_GET_CONTENT,
|
||||
SftpGetContentResponse.builder()
|
||||
.sessionId(sessionId)
|
||||
.result(BooleanBit.of(ex == null).getValue())
|
||||
.token(token)
|
||||
.msg(this.getErrorMessage(ex))
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package cd.casic.module.terminal.host.terminal.handler;
|
||||
|
||||
import cd.casic.module.terminal.common.BooleanBit;
|
||||
import cd.casic.module.terminal.host.terminal.enums.OutputTypeEnum;
|
||||
import cd.casic.module.terminal.host.terminal.model.request.SftpListRequest;
|
||||
import cd.casic.module.terminal.host.terminal.model.response.SftpFileVO;
|
||||
import cd.casic.module.terminal.host.terminal.model.response.SftpListResponse;
|
||||
import cd.casic.module.terminal.host.terminal.session.ISftpSession;
|
||||
import cn.orionsec.kit.lang.utils.collect.Lists;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* sftp 文件列表
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/29 15:32
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SftpListHandler extends AbstractTerminalHandler<SftpListRequest> {
|
||||
|
||||
private static final String HOME_PATH = "~";
|
||||
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, SftpListRequest payload) {
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
log.info("SftpListHandler-handle start sessionId: {}, path: {}", sessionId, path);
|
||||
Exception ex = null;
|
||||
List<SftpFileVO> list = Lists.empty();
|
||||
try {
|
||||
// 空目录则直接获取 home 目录
|
||||
if (HOME_PATH.equals(path)) {
|
||||
path = session.getHome();
|
||||
}
|
||||
// 文件列表
|
||||
list = session.list(path, BooleanBit.toBoolean(payload.getShowHiddenFile()));
|
||||
log.info("SftpListHandler-handle success sessionId: {}, path: {}", sessionId, path);
|
||||
} catch (Exception e) {
|
||||
log.error("SftpListHandler-handle error sessionId: {}", sessionId, e);
|
||||
ex = e;
|
||||
}
|
||||
// 返回
|
||||
this.send(channel,
|
||||
OutputTypeEnum.SFTP_LIST,
|
||||
SftpListResponse.builder()
|
||||
.sessionId(sessionId)
|
||||
.result(BooleanBit.of(ex == null).getValue())
|
||||
.path(path)
|
||||
.body(JSON.toJSONString(list))
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package cd.casic.module.terminal.host.terminal.handler;
|
||||
|
||||
import cd.casic.module.terminal.common.BooleanBit;
|
||||
import cd.casic.module.terminal.common.OperatorLogs;
|
||||
import cd.casic.module.terminal.define.operator.TerminalOperatorType;
|
||||
import cd.casic.module.terminal.host.terminal.enums.OutputTypeEnum;
|
||||
import cd.casic.module.terminal.host.terminal.model.request.SftpBaseRequest;
|
||||
import cd.casic.module.terminal.host.terminal.model.response.SftpBaseResponse;
|
||||
import cd.casic.module.terminal.host.terminal.session.ISftpSession;
|
||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* sftp 创建文件夹
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/19 11:13
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SftpMakeDirectoryHandler extends AbstractTerminalHandler<SftpBaseRequest> {
|
||||
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, SftpBaseRequest payload) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
log.info("SftpMakeDirectoryHandler-handle start sessionId: {}, path: {}", sessionId, path);
|
||||
Exception ex = null;
|
||||
// 创建文件夹
|
||||
try {
|
||||
session.mkdir(path);
|
||||
log.info("SftpMakeDirectoryHandler-handle success sessionId: {}, path: {}", sessionId, path);
|
||||
} catch (Exception e) {
|
||||
log.error("SftpMakeDirectoryHandler-handle error sessionId: {}", sessionId, e);
|
||||
ex = e;
|
||||
}
|
||||
// 返回
|
||||
this.send(channel,
|
||||
OutputTypeEnum.SFTP_MKDIR,
|
||||
SftpBaseResponse.builder()
|
||||
.sessionId(sessionId)
|
||||
.result(BooleanBit.of(ex == null).getValue())
|
||||
.msg(this.getErrorMessage(ex))
|
||||
.build());
|
||||
// 保存操作日志
|
||||
Map<String, Object> extra = Maps.newMap();
|
||||
extra.put(OperatorLogs.PATH, path);
|
||||
this.saveOperatorLog(payload, channel,
|
||||
extra, TerminalOperatorType.SFTP_MKDIR,
|
||||
startTime, ex);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package cd.casic.module.terminal.host.terminal.handler;
|
||||
|
||||
import cd.casic.module.terminal.common.BooleanBit;
|
||||
import cd.casic.module.terminal.common.OperatorLogs;
|
||||
import cd.casic.module.terminal.define.operator.TerminalOperatorType;
|
||||
import cd.casic.module.terminal.host.terminal.enums.OutputTypeEnum;
|
||||
import cd.casic.module.terminal.host.terminal.model.request.SftpMoveRequest;
|
||||
import cd.casic.module.terminal.host.terminal.model.response.SftpBaseResponse;
|
||||
import cd.casic.module.terminal.host.terminal.session.ISftpSession;
|
||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* sftp 移动文件
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/19 11:13
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SftpMoveHandler extends AbstractTerminalHandler<SftpMoveRequest> {
|
||||
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, SftpMoveRequest payload) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
String target = payload.getTarget();
|
||||
log.info("SftpMoveHandler-handle start sessionId: {}, path: {}, target: {}", sessionId, path, target);
|
||||
Exception ex = null;
|
||||
// 移动
|
||||
try {
|
||||
session.move(path, target);
|
||||
log.info("SftpMoveHandler-handle success sessionId: {}, path: {}, target: {}", sessionId, path, target);
|
||||
} catch (Exception e) {
|
||||
log.error("SftpMoveHandler-handle error sessionId: {}", sessionId, e);
|
||||
ex = e;
|
||||
}
|
||||
// 返回
|
||||
this.send(channel,
|
||||
OutputTypeEnum.SFTP_MOVE,
|
||||
SftpBaseResponse.builder()
|
||||
.sessionId(sessionId)
|
||||
.result(BooleanBit.of(ex == null).getValue())
|
||||
.msg(this.getErrorMessage(ex))
|
||||
.build());
|
||||
// 保存操作日志
|
||||
Map<String, Object> extra = Maps.newMap();
|
||||
extra.put(OperatorLogs.PATH, path);
|
||||
extra.put(OperatorLogs.TARGET, target);
|
||||
this.saveOperatorLog(payload, channel,
|
||||
extra, TerminalOperatorType.SFTP_MOVE,
|
||||
startTime, ex);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package cd.casic.module.terminal.host.terminal.handler;
|
||||
|
||||
import cd.casic.module.terminal.common.BooleanBit;
|
||||
import cd.casic.module.terminal.common.OperatorLogs;
|
||||
import cd.casic.module.terminal.define.operator.TerminalOperatorType;
|
||||
import cd.casic.module.terminal.host.terminal.enums.OutputTypeEnum;
|
||||
import cd.casic.module.terminal.host.terminal.model.request.SftpBaseRequest;
|
||||
import cd.casic.module.terminal.host.terminal.model.response.SftpBaseResponse;
|
||||
import cd.casic.module.terminal.host.terminal.session.ISftpSession;
|
||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* sftp 删除文件
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/19 11:13
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SftpRemoveHandler extends AbstractTerminalHandler<SftpBaseRequest> {
|
||||
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, SftpBaseRequest payload) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
String path = payload.getPath();
|
||||
String sessionId = payload.getSessionId();
|
||||
// 获取会话
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
String[] paths = path.split("\\|");
|
||||
log.info("SftpRemoveHandler-handle start sessionId: {}, path: {}", sessionId, Arrays.toString(paths));
|
||||
Exception ex = null;
|
||||
// 删除
|
||||
try {
|
||||
session.remove(paths);
|
||||
log.info("SftpRemoveHandler-handle success sessionId: {}, path: {}", sessionId, Arrays.toString(paths));
|
||||
} catch (Exception e) {
|
||||
log.error("SftpRemoveHandler-handle error sessionId: {}", sessionId, e);
|
||||
ex = e;
|
||||
}
|
||||
// 返回
|
||||
this.send(channel,
|
||||
OutputTypeEnum.SFTP_REMOVE,
|
||||
SftpBaseResponse.builder()
|
||||
.sessionId(sessionId)
|
||||
.result(BooleanBit.of(ex == null).getValue())
|
||||
.msg(this.getErrorMessage(ex))
|
||||
.build());
|
||||
// 保存操作日志
|
||||
Map<String, Object> extra = Maps.newMap();
|
||||
extra.put(OperatorLogs.PATH, path);
|
||||
this.saveOperatorLog(payload, channel,
|
||||
extra, TerminalOperatorType.SFTP_REMOVE,
|
||||
startTime, ex);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package cd.casic.module.terminal.host.terminal.handler;
|
||||
|
||||
import cd.casic.module.terminal.common.BooleanBit;
|
||||
import cd.casic.module.terminal.common.OperatorLogs;
|
||||
import cd.casic.module.terminal.dal.redis.TerminalRedisDAO;
|
||||
import cd.casic.module.terminal.define.cache.TerminalCacheKeyDefine;
|
||||
import cd.casic.module.terminal.define.operator.TerminalOperatorType;
|
||||
import cd.casic.module.terminal.host.terminal.dto.SftpSetContentCacheDTO;
|
||||
import cd.casic.module.terminal.host.terminal.enums.OutputTypeEnum;
|
||||
import cd.casic.module.terminal.host.terminal.model.request.SftpBaseRequest;
|
||||
import cd.casic.module.terminal.host.terminal.model.response.SftpSetContentResponse;
|
||||
import cd.casic.module.terminal.host.terminal.session.ISftpSession;
|
||||
import cn.orionsec.kit.lang.id.UUIds;
|
||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* sftp 设置文件内容
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/19 11:13
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SftpSetContentHandler extends AbstractTerminalHandler<SftpBaseRequest> {
|
||||
|
||||
@Resource
|
||||
private TerminalRedisDAO terminalRedisDAO;
|
||||
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, SftpBaseRequest payload) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
log.info("SftpSetContentHandler-handle start sessionId: {}, path: {}", sessionId, path);
|
||||
String token = UUIds.random32();
|
||||
Exception ex = null;
|
||||
try {
|
||||
// 检查文件是否可编辑
|
||||
session.checkCanEdit(path);
|
||||
// 设置缓存
|
||||
String key = TerminalCacheKeyDefine.TERMINAL_SFTP_SET_CONTENT.format(token);
|
||||
SftpSetContentCacheDTO cache = SftpSetContentCacheDTO.builder()
|
||||
.hostId(session.getConfig().getHostId())
|
||||
.path(path)
|
||||
.build();
|
||||
terminalRedisDAO.setJson(key, TerminalCacheKeyDefine.TERMINAL_SFTP_SET_CONTENT, cache);
|
||||
log.info("SftpSetContentHandler-handle success sessionId: {}, path: {}", sessionId, path);
|
||||
} catch (Exception e) {
|
||||
log.error("SftpSetContentHandler-handle error sessionId: {}", sessionId, e);
|
||||
ex = e;
|
||||
}
|
||||
// 返回
|
||||
this.send(channel,
|
||||
OutputTypeEnum.SFTP_SET_CONTENT,
|
||||
SftpSetContentResponse.builder()
|
||||
.sessionId(sessionId)
|
||||
.result(BooleanBit.of(ex == null).getValue())
|
||||
.token(token)
|
||||
.msg(this.getErrorMessage(ex))
|
||||
.build());
|
||||
// 保存操作日志
|
||||
Map<String, Object> extra = Maps.newMap();
|
||||
extra.put(OperatorLogs.PATH, path);
|
||||
this.saveOperatorLog(payload, channel,
|
||||
extra, TerminalOperatorType.SFTP_SET_CONTENT,
|
||||
startTime, ex);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package cd.casic.module.terminal.host.terminal.handler;
|
||||
|
||||
import cd.casic.module.terminal.common.BooleanBit;
|
||||
import cd.casic.module.terminal.common.OperatorLogs;
|
||||
import cd.casic.module.terminal.define.operator.TerminalOperatorType;
|
||||
import cd.casic.module.terminal.host.terminal.enums.OutputTypeEnum;
|
||||
import cd.casic.module.terminal.host.terminal.model.request.SftpBaseRequest;
|
||||
import cd.casic.module.terminal.host.terminal.model.response.SftpBaseResponse;
|
||||
import cd.casic.module.terminal.host.terminal.session.ISftpSession;
|
||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* sftp 创建文件
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/19 11:13
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SftpTouchHandler extends AbstractTerminalHandler<SftpBaseRequest> {
|
||||
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, SftpBaseRequest payload) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
log.info("SftpTouchHandler-handle start sessionId: {}, path: {}", sessionId, path);
|
||||
Exception ex = null;
|
||||
// 创建文件
|
||||
try {
|
||||
session.touch(path);
|
||||
log.info("SftpTouchHandler-handle success sessionId: {}, path: {}", sessionId, path);
|
||||
} catch (Exception e) {
|
||||
log.error("SftpTouchHandler-handle error sessionId: {}", sessionId, e);
|
||||
ex = e;
|
||||
}
|
||||
// 返回
|
||||
this.send(channel,
|
||||
OutputTypeEnum.SFTP_TOUCH,
|
||||
SftpBaseResponse.builder()
|
||||
.sessionId(sessionId)
|
||||
.result(BooleanBit.of(ex == null).getValue())
|
||||
.msg(this.getErrorMessage(ex))
|
||||
.build());
|
||||
// 保存操作日志
|
||||
Map<String, Object> extra = Maps.newMap();
|
||||
extra.put(OperatorLogs.PATH, path);
|
||||
this.saveOperatorLog(payload, channel,
|
||||
extra, TerminalOperatorType.SFTP_TOUCH,
|
||||
startTime, ex);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package cd.casic.module.terminal.host.terminal.handler;
|
||||
|
||||
import cd.casic.module.terminal.common.BooleanBit;
|
||||
import cd.casic.module.terminal.common.OperatorLogs;
|
||||
import cd.casic.module.terminal.define.operator.TerminalOperatorType;
|
||||
import cd.casic.module.terminal.host.terminal.enums.OutputTypeEnum;
|
||||
import cd.casic.module.terminal.host.terminal.model.request.SftpBaseRequest;
|
||||
import cd.casic.module.terminal.host.terminal.model.response.SftpBaseResponse;
|
||||
import cd.casic.module.terminal.host.terminal.session.ISftpSession;
|
||||
import cn.orionsec.kit.lang.utils.collect.Maps;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* sftp 截断文件
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2024/2/19 11:13
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SftpTruncateHandler extends AbstractTerminalHandler<SftpBaseRequest> {
|
||||
|
||||
@Override
|
||||
public void handle(WebSocketSession channel, SftpBaseRequest payload) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 获取会话
|
||||
String sessionId = payload.getSessionId();
|
||||
ISftpSession session = terminalManager.getSession(channel.getId(), sessionId);
|
||||
String path = payload.getPath();
|
||||
log.info("SftpTruncateHandler-handle start sessionId: {}, path: {}", sessionId, path);
|
||||
Exception ex = null;
|
||||
// 截断文件
|
||||
try {
|
||||
session.truncate(path);
|
||||
log.info("SftpTruncateHandler-handle success sessionId: {}, path: {}", sessionId, path);
|
||||
} catch (Exception e) {
|
||||
log.error("SftpTruncateHandler-handle error sessionId: {}", sessionId, e);
|
||||
ex = e;
|
||||
}
|
||||
// 返回
|
||||
this.send(channel,
|
||||
OutputTypeEnum.SFTP_TRUNCATE,
|
||||
SftpBaseResponse.builder()
|
||||
.sessionId(sessionId)
|
||||
.result(BooleanBit.of(ex == null).getValue())
|
||||
.msg(this.getErrorMessage(ex))
|
||||
.build());
|
||||
// 保存操作日志
|
||||
Map<String, Object> extra = Maps.newMap();
|
||||
extra.put(OperatorLogs.PATH, path);
|
||||
this.saveOperatorLog(payload, channel,
|
||||
extra, TerminalOperatorType.SFTP_TRUNCATE,
|
||||
startTime, ex);
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user