🎉 优化系统架构.

This commit is contained in:
lijiahangmax
2025-06-25 18:57:05 +08:00
parent f1ade56e13
commit cec7e21d5a
22 changed files with 667 additions and 118 deletions

View File

@@ -51,6 +51,11 @@ public interface Const extends cn.orionsec.kit.lang.constant.Const, FieldConst,
String SYSTEM_USERNAME = "system"; String SYSTEM_USERNAME = "system";
// FIXME KIT
String ADMINISTRATOR = "Administrator";
Long ALL_HOST_ID = -1L;
int BATCH_COUNT = 500; int BATCH_COUNT = 500;
} }

View File

@@ -88,7 +88,7 @@ public enum ErrorCode implements CodeInfo {
EXCEL_PASSWORD_ERROR(905, "文档密码错误"), EXCEL_PASSWORD_ERROR(905, "文档密码错误"),
PASER_FAILED(906, "解析失败"), PASER_FAILED(906, "表达式解析失败"),
ENCRYPT_ERROR(907, "数据加密异常"), ENCRYPT_ERROR(907, "数据加密异常"),
@@ -134,9 +134,10 @@ public enum ErrorCode implements CodeInfo {
* 获取 wapper * 获取 wapper
* *
* @param data data * @param data data
* @param <T> T
* @return HttpWrapper * @return HttpWrapper
*/ */
public HttpWrapper<?> wrapper() { public <T> HttpWrapper<T> wrapper() {
return HttpWrapper.of(this); return HttpWrapper.of(this);
} }

View File

@@ -24,6 +24,14 @@ package org.dromara.visor.common.constant;
import cn.orionsec.kit.lang.exception.ApplicationException; import cn.orionsec.kit.lang.exception.ApplicationException;
import cn.orionsec.kit.lang.exception.argument.InvalidArgumentException; import cn.orionsec.kit.lang.exception.argument.InvalidArgumentException;
import cn.orionsec.kit.lang.utils.Strings;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import javax.validation.ConstraintViolationException;
import java.util.Iterator;
import java.util.Optional;
import java.util.Set;
/** /**
* 错误信息 * 错误信息
@@ -52,6 +60,12 @@ public interface ErrorMessage {
String IDENTITY_ABSENT = "主机身份不存在"; String IDENTITY_ABSENT = "主机身份不存在";
String CHECK_IDENTITY_PASSWORD = "请选择类型为[密码]的主机身份";
String KEY_ABSENT_WITH = "主机密钥不存在 {}";
String IDENTITY_ABSENT_WITH = "主机身份不存在 {}";
String CONFIG_ABSENT = "配置不存在"; String CONFIG_ABSENT = "配置不存在";
String CONFIG_PRESENT = "配置已存在"; String CONFIG_PRESENT = "配置已存在";
@@ -96,24 +110,36 @@ public interface ErrorMessage {
String UNSUPPORTED_CHARSET = "不支持的编码 [{}]"; String UNSUPPORTED_CHARSET = "不支持的编码 [{}]";
String DECRYPT_ERROR = "数据解密失败";
String PASSWORD_MISSING = "请输入密码"; String PASSWORD_MISSING = "请输入密码";
String BEFORE_PASSWORD_ERROR = "原密码错误"; String BEFORE_PASSWORD_ERROR = "原密码错误";
String DATA_NO_PERMISSION = "数据无权限"; String DATA_NO_PERMISSION = "数据无权限";
String EXPRESSION_ERROR = "表达式错误";
String ANY_NO_PERMISSION = "{}无权限"; String ANY_NO_PERMISSION = "{}无权限";
String OPT_NO_PERMISSION = "无操作权限";
String SESSION_PRESENT = "会话已存在"; String SESSION_PRESENT = "会话已存在";
String SESSION_ABSENT = "会话不存在"; String SESSION_ABSENT = "会话不存在";
String SESSION_CLOSED = "会话已关闭";
String USER_UNSUPPORTED_OPT = "用户不支持此操作";
String CURRENT_USER_UNSUPPORTED_OPT = "当前" + USER_UNSUPPORTED_OPT;
String PATH_NOT_NORMALIZE = "路径不合法"; String PATH_NOT_NORMALIZE = "路径不合法";
String OPERATE_ERROR = "操作失败"; String OPERATE_ERROR = "操作失败";
String ENCRYPT_KEY_UNSET = "加密密钥未配置";
String DECRYPT_ERROR = "数据解密失败";
String UNKNOWN_TYPE = "未知类型"; String UNKNOWN_TYPE = "未知类型";
String ERROR_TYPE = "错误的类型"; String ERROR_TYPE = "错误的类型";
@@ -148,8 +174,26 @@ public interface ErrorMessage {
String CLIENT_ABORT = "手动中断"; String CLIENT_ABORT = "手动中断";
String COMMAND_EXEC_ERROR = "命令执行失败 [{}]";
String COMPRESS_ERROR = "压缩失败";
String DECOMPRESS_ERROR = "解压失败";
String COMPRESS_FILE_ABSENT = "压缩文件不存在";
String UNABLE_DOWNLOAD_FOLDER = "无法下载文件夹"; String UNABLE_DOWNLOAD_FOLDER = "无法下载文件夹";
String VALID_ERROR = "验证失败";
String CONVERT_ERROR = "转换失败";
String PRESENT_MODIFY = "{} 已存在, 请修改后重试";
String ILLEGAL_MODIFY = "{} 不正确, 请修改后重试";
String PLEASE_SELECT_SUFFIX_FILE = "请选择 {} 类型的文件";
/** /**
* 是否为业务异常 * 是否为业务异常
* *
@@ -187,4 +231,37 @@ public interface ErrorMessage {
return defaultMsg; return defaultMsg;
} }
/**
* 获取验证错误消息
*
* @param ex ex
* @param defaultMsg defaultMsg
* @return message
*/
static String getValidErrorMessage(Exception ex, String defaultMsg) {
if (ex == null) {
return null;
}
// 参数不存在异常
if (ex instanceof MissingServletRequestParameterException) {
return Strings.format(ErrorMessage.MISSING, ((MissingServletRequestParameterException) ex).getParameterName());
}
// 参数绑定异常
if (ex instanceof BindException) {
return Optional.ofNullable(((BindException) ex)
.getFieldError())
.map(error -> error.getField() + Const.SPACE + error.getDefaultMessage())
.orElse(defaultMsg);
}
// 参数验证异常
if (ex instanceof ConstraintViolationException) {
return Optional.ofNullable(((ConstraintViolationException) ex).getConstraintViolations())
.map(Set::iterator)
.map(Iterator::next)
.map(s -> s.getPropertyPath().toString() + Const.SPACE + s.getMessage())
.orElse(defaultMsg);
}
return getErrorMessage(ex, defaultMsg);
}
} }

View File

@@ -55,7 +55,7 @@ public interface ExtraFieldConst extends FieldConst {
String GRANT_NAME = "grantName"; String GRANT_NAME = "grantName";
String CHANNEL_ID = "channelId"; String CHANNEL = "channel";
String SESSION_ID = "sessionId"; String SESSION_ID = "sessionId";

View File

@@ -103,8 +103,20 @@ public interface FieldConst {
String FILTER = "filter"; String FILTER = "filter";
String LICENSE = "license";
String SESSION = "session";
String CONNECT = "connect";
String ALL = "all"; String ALL = "all";
String PROPS = "props";
String SENDER = "sender";
String RESULT = "result";
String CONFIG = "config"; String CONFIG = "config";
} }

View File

@@ -52,14 +52,26 @@ public enum EnableStatus {
public static EnableStatus of(Integer value) { public static EnableStatus of(Integer value) {
if (value == null) { if (value == null) {
return null; return DISABLED;
} }
for (EnableStatus e : values()) { for (EnableStatus e : values()) {
if (e.value.equals(value)) { if (e.value.equals(value)) {
return e; return e;
} }
} }
return null; return DISABLED;
}
public static EnableStatus of(String value) {
if (value == null) {
return DISABLED;
}
for (EnableStatus e : values()) {
if (e.name().equals(value)) {
return e;
}
}
return DISABLED;
} }
} }

View File

@@ -0,0 +1,148 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.enums;
import cn.orionsec.kit.lang.utils.collect.Lists;
import cn.orionsec.kit.lang.utils.time.DateStream;
import cn.orionsec.kit.lang.utils.time.Dates;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* 统计区间枚举
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/12/23 14:02
*/
public enum StatisticsRange {
/**
* 当天
*/
TODAY {
@Override
public Date getRangeEndTime(Date startTime) {
return DateStream.of(startTime)
.dayEnd()
.date();
}
@Override
public List<String> getDateRanges(Date startTime) {
return Lists.singleton(Dates.format(startTime, Dates.YMD));
}
},
/**
* 日视图
*/
DAY {
@Override
public Date getRangeEndTime(Date startTime) {
return DateStream.of(startTime)
.dayEnd()
.date();
}
@Override
public List<String> getDateRanges(Date startTime) {
return Lists.singleton(Dates.format(startTime, Dates.YMD));
}
},
/**
* 周视图
*/
WEEK {
@Override
public Date getRangeEndTime(Date startTime) {
return DateStream.of(startTime)
.addDay(7)
.dayEnd()
.date();
}
@Override
public List<String> getDateRanges(Date startTime) {
return Arrays.stream(Dates.getIncrementDayDates(startTime, 1, 7))
.map(s -> Dates.format(s, Dates.YMD))
.collect(Collectors.toList());
}
},
/**
* 月视图
*/
MONTH {
@Override
public Date getRangeEndTime(Date startTime) {
return DateStream.of(startTime)
.addMonth(1)
.dayEnd()
.date();
}
@Override
public List<String> getDateRanges(Date startTime) {
int monthLastDay = Dates.getMonthLastDay(startTime);
return Arrays.stream(Dates.getIncrementDayDates(startTime, 1, monthLastDay - 1))
.map(s -> Dates.format(s, Dates.YMD))
.collect(Collectors.toList());
}
},
;
/**
* 获取区间结束时间
*
* @param startTime startTime
* @return end
*/
public abstract Date getRangeEndTime(Date startTime);
/**
* 获取时间区间
*
* @param startTime startTime
* @return ranges
*/
public abstract List<String> getDateRanges(Date startTime);
public static StatisticsRange of(String type) {
if (type == null) {
return TODAY;
}
for (StatisticsRange value : values()) {
if (value.name().equals(type)) {
return value;
}
}
return TODAY;
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.session;
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 org.dromara.visor.common.constant.Const;
/**
* 连接消息
*
* @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 = "连接服务器超时. {}";
/**
* 获取错误信息
*
* @param address address
* @param e e
* @return errorMessage
*/
static String getErrorMessage(String address, Exception e) {
if (e == null) {
return null;
}
String message = e.getMessage();
if (Strings.contains(message, Const.TIMEOUT)) {
// 连接超时
return Strings.format(SessionMessage.CONNECTION_TIMEOUT, address);
} else if (Exceptions.isCausedBy(e, AuthenticationException.class)) {
// 认证失败
return Strings.format(SessionMessage.AUTHENTICATION_FAILURE, address);
} else if (Exceptions.isCausedBy(e, InvalidArgumentException.class)) {
// 参数错误
if (Strings.isBlank(message)) {
return Strings.format(SessionMessage.SERVER_UNREACHABLE, address);
} else {
return message;
}
} else {
// 其他错误
return Strings.format(SessionMessage.SERVER_UNREACHABLE, address);
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.session.config;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* 基础连接配置实现
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/4/1 16:59
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "BaseConnectConfig", description = "基础连接参数")
public class BaseConnectConfig implements IBaseConnectConfig {
@Schema(description = "系统类型")
private String osType;
@Schema(description = "系统架构")
private String archType;
@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 username;
@Schema(description = "密码")
private String password;
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.session.config;
/**
* 基础连接配置定义
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/6/24 17:11
*/
public interface IBaseConnectConfig {
// -------------------- getter/setter --------------------
String getOsType();
void setOsType(String osType);
String getArchType();
void setArchType(String archType);
Long getHostId();
void setHostId(Long hostId);
String getHostName();
void setHostName(String hostName);
String getHostCode();
void setHostCode(String hostCode);
String getHostAddress();
void setHostAddress(String hostAddress);
Integer getHostPort();
void setHostPort(Integer hostPort);
String getUsername();
void setUsername(String username);
String getPassword();
void setPassword(String password);
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2023 - present Dromara, All rights reserved.
*
* https://visor.dromara.org
* https://visor.dromara.org.cn
* https://visor.orionsec.cn
*
* Members:
* Jiahang Li - ljh1553488six@139.com - author
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.dromara.visor.common.session.config;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* RDP 连接参数
*
* @author Jiahang Li
* @version 1.0.0
* @since 2025/4/1 16:57
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Schema(name = "RdpConnectConfig", description = "RDP 连接参数")
public class RdpConnectConfig extends BaseConnectConfig {
@Schema(description = "低带宽模式")
private Boolean lowBandwidthMode;
@Schema(description = "RDP 版本是否大于8.1")
private Boolean versionGt81;
@Schema(description = "时区")
private String timezone;
@Schema(description = "键盘布局")
private String keyboardLayout;
@Schema(description = "剪切板规范")
private String clipboardNormalize;
@Schema(description = "")
private String domain;
@Schema(description = "预连接id")
private String preConnectionId;
@Schema(description = "预连接数据")
private String preConnectionBlob;
@Schema(description = "远程应用")
private String remoteApp;
@Schema(description = "远程应用路径")
private String remoteAppDir;
@Schema(description = "远程应用参数")
private String remoteAppArgs;
}

View File

@@ -20,57 +20,29 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.dromara.visor.module.asset.entity.dto; package org.dromara.visor.common.session.config;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.dromara.visor.framework.desensitize.core.annotation.Desensitize; import lombok.experimental.SuperBuilder;
import org.dromara.visor.framework.desensitize.core.annotation.DesensitizeObject;
/** /**
* 终端连接参数 * SSH 连接参数
* *
* @author Jiahang Li * @author Jiahang Li
* @version 1.0.0 * @version 1.0.0
* @since 2023/12/26 15:47 * @since 2023/12/26 15:47
*/ */
@Data @Data
@Builder @SuperBuilder
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@DesensitizeObject @EqualsAndHashCode(callSuper = true)
@Schema(name = "TerminalConnectDTO", description = "终端连接参数") @Schema(name = "SshConnectConfig", description = "SSH 连接参数")
public class TerminalConnectDTO { public class SshConnectConfig extends BaseConnectConfig {
@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 = "超时时间") @Schema(description = "超时时间")
private Integer timeout; private Integer timeout;
@@ -84,25 +56,15 @@ public class TerminalConnectDTO {
@Schema(description = "文件内容编码") @Schema(description = "文件内容编码")
private String fileContentCharset; private String fileContentCharset;
@Schema(description = "用户名")
private String username;
@Desensitize(toEmpty = true)
@Schema(description = "密码")
private String password;
@Schema(description = "密钥id") @Schema(description = "密钥id")
private Long keyId; private Long keyId;
@Desensitize(toEmpty = true)
@Schema(description = "公钥文本") @Schema(description = "公钥文本")
private String publicKey; private String publicKey;
@Desensitize(toEmpty = true)
@Schema(description = "私钥文本") @Schema(description = "私钥文本")
private String privateKey; private String privateKey;
@Desensitize(toEmpty = true)
@Schema(description = "私钥密码") @Schema(description = "私钥密码")
private String privateKeyPassword; private String privateKeyPassword;

View File

@@ -20,10 +20,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.dromara.visor.module.asset.handler.host.jsch; package org.dromara.visor.common.session.ssh;
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.Exceptions;
import cn.orionsec.kit.lang.utils.Strings; import cn.orionsec.kit.lang.utils.Strings;
import cn.orionsec.kit.net.host.SessionHolder; import cn.orionsec.kit.net.host.SessionHolder;
@@ -31,9 +29,9 @@ import cn.orionsec.kit.net.host.SessionLogger;
import cn.orionsec.kit.net.host.SessionStore; import cn.orionsec.kit.net.host.SessionStore;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.visor.common.constant.AppConst; import org.dromara.visor.common.constant.AppConst;
import org.dromara.visor.common.constant.Const; import org.dromara.visor.common.session.SessionMessage;
import org.dromara.visor.common.session.config.SshConnectConfig;
import org.dromara.visor.common.utils.AesEncryptUtils; import org.dromara.visor.common.utils.AesEncryptUtils;
import org.dromara.visor.module.asset.entity.dto.TerminalConnectDTO;
import java.util.Optional; import java.util.Optional;
@@ -47,25 +45,22 @@ import java.util.Optional;
@Slf4j @Slf4j
public class SessionStores { public class SessionStores {
protected static final ThreadLocal<String> CURRENT_ADDRESS = new ThreadLocal<>();
/** /**
* 打开 sessionStore * 打开 sessionStore
* *
* @param conn conn * @param config config
* @return sessionStore * @return sessionStore
*/ */
public static SessionStore openSessionStore(TerminalConnectDTO conn) { public static SessionStore openSessionStore(SshConnectConfig config) {
Long hostId = conn.getHostId(); Long hostId = config.getHostId();
String address = conn.getHostAddress(); String address = config.getHostAddress();
String username = conn.getUsername(); String username = config.getUsername();
log.info("SessionStores-open-start hostId: {}, address: {}, username: {}", hostId, address, username); log.info("SessionStores-open-start hostId: {}, address: {}, username: {}", hostId, address, username);
try { try {
CURRENT_ADDRESS.set(address);
// 创建会话 // 创建会话
SessionHolder sessionHolder = SessionHolder.create(); SessionHolder sessionHolder = SessionHolder.create();
sessionHolder.setLogger(SessionLogger.INFO); sessionHolder.setLogger(SessionLogger.INFO);
SessionStore session = createSessionStore(conn, sessionHolder); SessionStore session = createSessionStore(config, sessionHolder);
// 设置版本 // 设置版本
session.getSession().setClientVersion("SSH-2.0-ORION_VISOR_V" + AppConst.VERSION); session.getSession().setClientVersion("SSH-2.0-ORION_VISOR_V" + AppConst.VERSION);
// 连接 // 连接
@@ -75,81 +70,48 @@ public class SessionStores {
} catch (Exception e) { } catch (Exception e) {
String message = e.getMessage(); String message = e.getMessage();
log.error("SessionStores-open-error hostId: {}, address: {}, username: {}, message: {}", hostId, address, username, message, e); log.error("SessionStores-open-error hostId: {}, address: {}, username: {}, message: {}", hostId, address, username, message, e);
throw Exceptions.app(getErrorMessage(e), e); throw Exceptions.app(SessionMessage.getErrorMessage(address, e), e);
} finally {
CURRENT_ADDRESS.remove();
} }
} }
/** /**
* 创建 sessionStore * 创建 sessionStore
* *
* @param conn conn * @param config config
* @param sessionHolder sessionHolder * @param sessionHolder sessionHolder
* @return sessionStore * @return sessionStore
*/ */
private static SessionStore createSessionStore(TerminalConnectDTO conn, SessionHolder sessionHolder) { private static SessionStore createSessionStore(SshConnectConfig config, SessionHolder sessionHolder) {
final boolean useKey = conn.getKeyId() != null; final boolean useKey = config.getKeyId() != null;
// 使用密钥认证 // 使用密钥认证
if (useKey) { if (useKey) {
// 加载密钥 // 加载密钥
String publicKey = Optional.ofNullable(conn.getPublicKey()) String publicKey = Optional.ofNullable(config.getPublicKey())
.map(AesEncryptUtils::decryptAsString) .map(AesEncryptUtils::decryptAsString)
.orElse(null); .orElse(null);
String privateKey = Optional.ofNullable(conn.getPrivateKey()) String privateKey = Optional.ofNullable(config.getPrivateKey())
.map(AesEncryptUtils::decryptAsString) .map(AesEncryptUtils::decryptAsString)
.orElse(null); .orElse(null);
String password = Optional.ofNullable(conn.getPrivateKeyPassword()) String password = Optional.ofNullable(config.getPrivateKeyPassword())
.map(AesEncryptUtils::decryptAsString) .map(AesEncryptUtils::decryptAsString)
.orElse(null); .orElse(null);
sessionHolder.addIdentityValue(String.valueOf(conn.getKeyId()), sessionHolder.addIdentityValue(String.valueOf(config.getKeyId()),
privateKey, privateKey,
publicKey, publicKey,
password); password);
} }
// 获取会话 // 获取会话
SessionStore session = sessionHolder.getSession(conn.getHostAddress(), conn.getHostPort(), conn.getUsername()); SessionStore session = sessionHolder.getSession(config.getHostAddress(), config.getHostPort(), config.getUsername());
// 使用密码认证 // 使用密码认证
if (!useKey) { if (!useKey) {
String password = conn.getPassword(); String password = config.getPassword();
if (!Strings.isEmpty(password)) { if (!Strings.isEmpty(password)) {
session.password(AesEncryptUtils.decryptAsString(password)); session.password(AesEncryptUtils.decryptAsString(password));
} }
} }
// 超时时间 // 超时时间
session.timeout(conn.getTimeout()); session.timeout(config.getTimeout());
return session; 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);
}
}
} }

View File

@@ -22,7 +22,7 @@
*/ */
package org.dromara.visor.common.utils; package org.dromara.visor.common.utils;
import cn.orionsec.kit.lang.constant.Const; import org.dromara.visor.common.constant.Const;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;

View File

@@ -43,6 +43,9 @@ public class SqlUtils {
* @return limit * @return limit
*/ */
public static String limit(Number limit) { public static String limit(Number limit) {
if (limit == null) {
return Const.EMPTY;
}
return Const.LIMIT + Const.SPACE + limit; return Const.LIMIT + Const.SPACE + limit;
} }
@@ -54,6 +57,9 @@ public class SqlUtils {
* @return limit * @return limit
*/ */
public static String limit(Number offset, Number limit) { public static String limit(Number offset, Number limit) {
if (offset == null) {
return limit(limit);
}
return Const.LIMIT + Const.SPACE + offset + Const.COMMA + limit; return Const.LIMIT + Const.SPACE + offset + Const.COMMA + limit;
} }

View File

@@ -25,6 +25,7 @@ package org.dromara.visor.common.utils;
import cn.orionsec.kit.lang.utils.Arrays1; import cn.orionsec.kit.lang.utils.Arrays1;
import cn.orionsec.kit.lang.utils.io.Files1; import cn.orionsec.kit.lang.utils.io.Files1;
import cn.orionsec.kit.spring.SpringHolder; import cn.orionsec.kit.spring.SpringHolder;
import org.dromara.visor.common.constant.Const;
import org.dromara.visor.common.constant.ErrorMessage; import org.dromara.visor.common.constant.ErrorMessage;
import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolation;
@@ -159,4 +160,17 @@ public class Valid extends cn.orionsec.kit.lang.utils.Valid {
return Files1.getPath(path); return Files1.getPath(path);
} }
/**
* 检查后缀
*
* @param file file
* @param suffix suffix
* @return file
*/
public static String checkSuffix(String file, String suffix) {
Valid.notBlank(file);
Valid.isTrue(file.toLowerCase().endsWith(Const.DOT + suffix), ErrorMessage.PLEASE_SELECT_SUFFIX_FILE, suffix);
return file;
}
} }

View File

@@ -14,7 +14,7 @@
<url>https://github.com/dromara/orion-visor</url> <url>https://github.com/dromara/orion-visor</url>
<properties> <properties>
<revision>2.3.9</revision> <revision>2.4.0</revision>
<spring.boot.version>2.7.17</spring.boot.version> <spring.boot.version>2.7.17</spring.boot.version>
<spring.boot.admin.version>2.7.15</spring.boot.admin.version> <spring.boot.admin.version>2.7.15</spring.boot.admin.version>
<flatten.maven.plugin.version>1.5.0</flatten.maven.plugin.version> <flatten.maven.plugin.version>1.5.0</flatten.maven.plugin.version>
@@ -34,6 +34,7 @@
<mockito.inline.version>4.11.0</mockito.inline.version> <mockito.inline.version>4.11.0</mockito.inline.version>
<jedis.mock.version>1.0.7</jedis.mock.version> <jedis.mock.version>1.0.7</jedis.mock.version>
<podam.version>7.2.11.RELEASE</podam.version> <podam.version>7.2.11.RELEASE</podam.version>
<guacd.version>1.5.5</guacd.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
@@ -312,6 +313,13 @@
<artifactId>podam</artifactId> <artifactId>podam</artifactId>
<version>${podam.version}</version> <version>${podam.version}</version>
</dependency> </dependency>
<!-- guacd -->
<dependency>
<groupId>org.apache.guacamole</groupId>
<artifactId>guacamole-common</artifactId>
<version>${guacd.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

View File

@@ -23,13 +23,26 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<!-- starter --> <!-- spring boot starter -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId> <artifactId>spring-boot-starter</artifactId>
</dependency> </dependency>
<!-- modules --> <!-- common -->
<dependency>
<groupId>org.dromara.visor</groupId>
<artifactId>orion-visor-common</artifactId>
</dependency>
<!-- module common -->
<dependency>
<groupId>org.dromara.visor</groupId>
<artifactId>orion-visor-module-common</artifactId>
<version>${revision}</version>
</dependency>
<!-- module service -->
<dependency> <dependency>
<groupId>org.dromara.visor</groupId> <groupId>org.dromara.visor</groupId>
<artifactId>orion-visor-module-infra-service</artifactId> <artifactId>orion-visor-module-infra-service</artifactId>
@@ -40,6 +53,16 @@
<artifactId>orion-visor-module-asset-service</artifactId> <artifactId>orion-visor-module-asset-service</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency>
<groupId>org.dromara.visor</groupId>
<artifactId>orion-visor-module-exec-service</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.dromara.visor</groupId>
<artifactId>orion-visor-module-terminal-service</artifactId>
<version>${revision}</version>
</dependency>
<!-- framework starter --> <!-- framework starter -->
<dependency> <dependency>

View File

@@ -24,6 +24,11 @@ spring:
server: server:
enabled: false enabled: false
guacd:
host: ${GUACD_HOST:127.0.0.1}
port: ${GUACD_PORT:4822}
drive-path: ${GUACD_DRIVE_PATH:/home/guacd}
management: management:
endpoints: endpoints:
enabled-by-default: false enabled-by-default: false
@@ -33,7 +38,7 @@ management:
mybatis-plus: mybatis-plus:
configuration: configuration:
# sql 日志打印 # 日志打印
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
no: no:

View File

@@ -41,6 +41,11 @@ spring:
server: server:
enabled: true enabled: true
guacd:
host: ${GUACD_HOST:127.0.0.1}
port: ${GUACD_PORT:4822}
drive-path: ${GUACD_DRIVE_PATH:/usr/share/guacd/drive}
management: management:
endpoints: endpoints:
enabled-by-default: true enabled-by-default: true

View File

@@ -196,6 +196,12 @@ orion:
asset: asset:
group: "asset - 资产模块" group: "asset - 资产模块"
path: "asset" path: "asset"
exec:
group: "exec - 执行模块"
path: "exec"
terminal:
group: "terminal - 终端模块"
path: "terminal"
logging: logging:
# 全局日志打印 # 全局日志打印
printer: printer:
@@ -208,6 +214,7 @@ orion:
field: field:
ignore: ignore:
- password,beforePassword,newPassword,useNewPassword,publicKey,privateKey,privateKeyPassword - password,beforePassword,newPassword,useNewPassword,publicKey,privateKey,privateKeyPassword
- accessKey,secretKey
- metrics - metrics
desensitize: desensitize:
storage: storage:

View File

@@ -39,9 +39,9 @@ import java.util.function.Function;
*/ */
public class ReplaceVersion { public class ReplaceVersion {
private static final String TARGET_VERSION = "2.3.8"; private static final String TARGET_VERSION = "2.3.9";
private static final String REPLACE_VERSION = "2.3.9"; private static final String REPLACE_VERSION = "2.4.0";
private static final String PATH = new File("").getAbsolutePath(); private static final String PATH = new File("").getAbsolutePath();
@@ -50,6 +50,7 @@ public class ReplaceVersion {
"docker/adminer/build.sh", "docker/adminer/build.sh",
"docker/mysql/build.sh", "docker/mysql/build.sh",
"docker/redis/build.sh", "docker/redis/build.sh",
"docker/guacd/build.sh",
"docker/service/build.sh", "docker/service/build.sh",
"docker/ui/build.sh", "docker/ui/build.sh",
"docker-compose.yml", "docker-compose.yml",