🎉 优化系统架构.
This commit is contained in:
@@ -51,6 +51,11 @@ public interface Const extends cn.orionsec.kit.lang.constant.Const, FieldConst,
|
||||
|
||||
String SYSTEM_USERNAME = "system";
|
||||
|
||||
// FIXME KIT
|
||||
String ADMINISTRATOR = "Administrator";
|
||||
|
||||
Long ALL_HOST_ID = -1L;
|
||||
|
||||
int BATCH_COUNT = 500;
|
||||
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ public enum ErrorCode implements CodeInfo {
|
||||
|
||||
EXCEL_PASSWORD_ERROR(905, "文档密码错误"),
|
||||
|
||||
PASER_FAILED(906, "解析失败"),
|
||||
PASER_FAILED(906, "表达式解析失败"),
|
||||
|
||||
ENCRYPT_ERROR(907, "数据加密异常"),
|
||||
|
||||
@@ -134,9 +134,10 @@ public enum ErrorCode implements CodeInfo {
|
||||
* 获取 wapper
|
||||
*
|
||||
* @param data data
|
||||
* @param <T> T
|
||||
* @return HttpWrapper
|
||||
*/
|
||||
public HttpWrapper<?> wrapper() {
|
||||
public <T> HttpWrapper<T> wrapper() {
|
||||
return HttpWrapper.of(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,14 @@ package org.dromara.visor.common.constant;
|
||||
|
||||
import cn.orionsec.kit.lang.exception.ApplicationException;
|
||||
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 CHECK_IDENTITY_PASSWORD = "请选择类型为[密码]的主机身份";
|
||||
|
||||
String KEY_ABSENT_WITH = "主机密钥不存在 {}";
|
||||
|
||||
String IDENTITY_ABSENT_WITH = "主机身份不存在 {}";
|
||||
|
||||
String CONFIG_ABSENT = "配置不存在";
|
||||
|
||||
String CONFIG_PRESENT = "配置已存在";
|
||||
@@ -96,24 +110,36 @@ public interface ErrorMessage {
|
||||
|
||||
String UNSUPPORTED_CHARSET = "不支持的编码 [{}]";
|
||||
|
||||
String DECRYPT_ERROR = "数据解密失败";
|
||||
|
||||
String PASSWORD_MISSING = "请输入密码";
|
||||
|
||||
String BEFORE_PASSWORD_ERROR = "原密码错误";
|
||||
|
||||
String DATA_NO_PERMISSION = "数据无权限";
|
||||
|
||||
String EXPRESSION_ERROR = "表达式错误";
|
||||
|
||||
String ANY_NO_PERMISSION = "{}无权限";
|
||||
|
||||
String OPT_NO_PERMISSION = "无操作权限";
|
||||
|
||||
String SESSION_PRESENT = "会话已存在";
|
||||
|
||||
String SESSION_ABSENT = "会话不存在";
|
||||
|
||||
String SESSION_CLOSED = "会话已关闭";
|
||||
|
||||
String USER_UNSUPPORTED_OPT = "用户不支持此操作";
|
||||
|
||||
String CURRENT_USER_UNSUPPORTED_OPT = "当前" + USER_UNSUPPORTED_OPT;
|
||||
|
||||
String PATH_NOT_NORMALIZE = "路径不合法";
|
||||
|
||||
String OPERATE_ERROR = "操作失败";
|
||||
|
||||
String ENCRYPT_KEY_UNSET = "加密密钥未配置";
|
||||
|
||||
String DECRYPT_ERROR = "数据解密失败";
|
||||
|
||||
String UNKNOWN_TYPE = "未知类型";
|
||||
|
||||
String ERROR_TYPE = "错误的类型";
|
||||
@@ -148,8 +174,26 @@ public interface ErrorMessage {
|
||||
|
||||
String CLIENT_ABORT = "手动中断";
|
||||
|
||||
String COMMAND_EXEC_ERROR = "命令执行失败 [{}]";
|
||||
|
||||
String COMPRESS_ERROR = "压缩失败";
|
||||
|
||||
String DECOMPRESS_ERROR = "解压失败";
|
||||
|
||||
String COMPRESS_FILE_ABSENT = "压缩文件不存在";
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取验证错误消息
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public interface ExtraFieldConst extends FieldConst {
|
||||
|
||||
String GRANT_NAME = "grantName";
|
||||
|
||||
String CHANNEL_ID = "channelId";
|
||||
String CHANNEL = "channel";
|
||||
|
||||
String SESSION_ID = "sessionId";
|
||||
|
||||
|
||||
@@ -103,8 +103,20 @@ public interface FieldConst {
|
||||
|
||||
String FILTER = "filter";
|
||||
|
||||
String LICENSE = "license";
|
||||
|
||||
String SESSION = "session";
|
||||
|
||||
String CONNECT = "connect";
|
||||
|
||||
String ALL = "all";
|
||||
|
||||
String PROPS = "props";
|
||||
|
||||
String SENDER = "sender";
|
||||
|
||||
String RESULT = "result";
|
||||
|
||||
String CONFIG = "config";
|
||||
|
||||
}
|
||||
|
||||
@@ -52,14 +52,26 @@ public enum EnableStatus {
|
||||
|
||||
public static EnableStatus of(Integer value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
return DISABLED;
|
||||
}
|
||||
for (EnableStatus e : values()) {
|
||||
if (e.value.equals(value)) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -20,57 +20,29 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* 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 lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.visor.framework.desensitize.core.annotation.Desensitize;
|
||||
import org.dromara.visor.framework.desensitize.core.annotation.DesensitizeObject;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* 终端连接参数
|
||||
* SSH 连接参数
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/12/26 15:47
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@SuperBuilder
|
||||
@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;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(name = "SshConnectConfig", description = "SSH 连接参数")
|
||||
public class SshConnectConfig extends BaseConnectConfig {
|
||||
|
||||
@Schema(description = "超时时间")
|
||||
private Integer timeout;
|
||||
@@ -84,25 +56,15 @@ public class TerminalConnectDTO {
|
||||
@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;
|
||||
|
||||
@@ -20,10 +20,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* 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.Strings;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
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.module.asset.entity.dto.TerminalConnectDTO;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -47,25 +45,22 @@ import java.util.Optional;
|
||||
@Slf4j
|
||||
public class SessionStores {
|
||||
|
||||
protected static final ThreadLocal<String> CURRENT_ADDRESS = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 打开 sessionStore
|
||||
*
|
||||
* @param conn conn
|
||||
* @param config config
|
||||
* @return sessionStore
|
||||
*/
|
||||
public static SessionStore openSessionStore(TerminalConnectDTO conn) {
|
||||
Long hostId = conn.getHostId();
|
||||
String address = conn.getHostAddress();
|
||||
String username = conn.getUsername();
|
||||
public static SessionStore openSessionStore(SshConnectConfig config) {
|
||||
Long hostId = config.getHostId();
|
||||
String address = config.getHostAddress();
|
||||
String username = config.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);
|
||||
SessionStore session = createSessionStore(config, sessionHolder);
|
||||
// 设置版本
|
||||
session.getSession().setClientVersion("SSH-2.0-ORION_VISOR_V" + AppConst.VERSION);
|
||||
// 连接
|
||||
@@ -75,81 +70,48 @@ public class SessionStores {
|
||||
} 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();
|
||||
throw Exceptions.app(SessionMessage.getErrorMessage(address, e), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 sessionStore
|
||||
*
|
||||
* @param conn conn
|
||||
* @param config config
|
||||
* @param sessionHolder sessionHolder
|
||||
* @return sessionStore
|
||||
*/
|
||||
private static SessionStore createSessionStore(TerminalConnectDTO conn, SessionHolder sessionHolder) {
|
||||
final boolean useKey = conn.getKeyId() != null;
|
||||
private static SessionStore createSessionStore(SshConnectConfig config, SessionHolder sessionHolder) {
|
||||
final boolean useKey = config.getKeyId() != null;
|
||||
// 使用密钥认证
|
||||
if (useKey) {
|
||||
// 加载密钥
|
||||
String publicKey = Optional.ofNullable(conn.getPublicKey())
|
||||
String publicKey = Optional.ofNullable(config.getPublicKey())
|
||||
.map(AesEncryptUtils::decryptAsString)
|
||||
.orElse(null);
|
||||
String privateKey = Optional.ofNullable(conn.getPrivateKey())
|
||||
String privateKey = Optional.ofNullable(config.getPrivateKey())
|
||||
.map(AesEncryptUtils::decryptAsString)
|
||||
.orElse(null);
|
||||
String password = Optional.ofNullable(conn.getPrivateKeyPassword())
|
||||
String password = Optional.ofNullable(config.getPrivateKeyPassword())
|
||||
.map(AesEncryptUtils::decryptAsString)
|
||||
.orElse(null);
|
||||
sessionHolder.addIdentityValue(String.valueOf(conn.getKeyId()),
|
||||
sessionHolder.addIdentityValue(String.valueOf(config.getKeyId()),
|
||||
privateKey,
|
||||
publicKey,
|
||||
password);
|
||||
}
|
||||
// 获取会话
|
||||
SessionStore session = sessionHolder.getSession(conn.getHostAddress(), conn.getHostPort(), conn.getUsername());
|
||||
SessionStore session = sessionHolder.getSession(config.getHostAddress(), config.getHostPort(), config.getUsername());
|
||||
// 使用密码认证
|
||||
if (!useKey) {
|
||||
String password = conn.getPassword();
|
||||
String password = config.getPassword();
|
||||
if (!Strings.isEmpty(password)) {
|
||||
session.password(AesEncryptUtils.decryptAsString(password));
|
||||
}
|
||||
}
|
||||
// 超时时间
|
||||
session.timeout(conn.getTimeout());
|
||||
session.timeout(config.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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
*/
|
||||
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.List;
|
||||
|
||||
@@ -43,6 +43,9 @@ public class SqlUtils {
|
||||
* @return limit
|
||||
*/
|
||||
public static String limit(Number limit) {
|
||||
if (limit == null) {
|
||||
return Const.EMPTY;
|
||||
}
|
||||
return Const.LIMIT + Const.SPACE + limit;
|
||||
}
|
||||
|
||||
@@ -54,6 +57,9 @@ public class SqlUtils {
|
||||
* @return limit
|
||||
*/
|
||||
public static String limit(Number offset, Number limit) {
|
||||
if (offset == null) {
|
||||
return limit(limit);
|
||||
}
|
||||
return Const.LIMIT + Const.SPACE + offset + Const.COMMA + limit;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ package org.dromara.visor.common.utils;
|
||||
import cn.orionsec.kit.lang.utils.Arrays1;
|
||||
import cn.orionsec.kit.lang.utils.io.Files1;
|
||||
import cn.orionsec.kit.spring.SpringHolder;
|
||||
import org.dromara.visor.common.constant.Const;
|
||||
import org.dromara.visor.common.constant.ErrorMessage;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
@@ -159,4 +160,17 @@ public class Valid extends cn.orionsec.kit.lang.utils.Valid {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<url>https://github.com/dromara/orion-visor</url>
|
||||
|
||||
<properties>
|
||||
<revision>2.3.9</revision>
|
||||
<revision>2.4.0</revision>
|
||||
<spring.boot.version>2.7.17</spring.boot.version>
|
||||
<spring.boot.admin.version>2.7.15</spring.boot.admin.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>
|
||||
<jedis.mock.version>1.0.7</jedis.mock.version>
|
||||
<podam.version>7.2.11.RELEASE</podam.version>
|
||||
<guacd.version>1.5.5</guacd.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@@ -312,6 +313,13 @@
|
||||
<artifactId>podam</artifactId>
|
||||
<version>${podam.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- guacd -->
|
||||
<dependency>
|
||||
<groupId>org.apache.guacamole</groupId>
|
||||
<artifactId>guacamole-common</artifactId>
|
||||
<version>${guacd.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@@ -23,13 +23,26 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- starter -->
|
||||
<!-- spring boot starter -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</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>
|
||||
<groupId>org.dromara.visor</groupId>
|
||||
<artifactId>orion-visor-module-infra-service</artifactId>
|
||||
@@ -40,6 +53,16 @@
|
||||
<artifactId>orion-visor-module-asset-service</artifactId>
|
||||
<version>${revision}</version>
|
||||
</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 -->
|
||||
<dependency>
|
||||
|
||||
@@ -24,6 +24,11 @@ spring:
|
||||
server:
|
||||
enabled: false
|
||||
|
||||
guacd:
|
||||
host: ${GUACD_HOST:127.0.0.1}
|
||||
port: ${GUACD_PORT:4822}
|
||||
drive-path: ${GUACD_DRIVE_PATH:/home/guacd}
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
enabled-by-default: false
|
||||
@@ -33,7 +38,7 @@ management:
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
# sql 日志打印
|
||||
# 日志打印
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
|
||||
no:
|
||||
|
||||
@@ -41,6 +41,11 @@ spring:
|
||||
server:
|
||||
enabled: true
|
||||
|
||||
guacd:
|
||||
host: ${GUACD_HOST:127.0.0.1}
|
||||
port: ${GUACD_PORT:4822}
|
||||
drive-path: ${GUACD_DRIVE_PATH:/usr/share/guacd/drive}
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
enabled-by-default: true
|
||||
|
||||
@@ -196,6 +196,12 @@ orion:
|
||||
asset:
|
||||
group: "asset - 资产模块"
|
||||
path: "asset"
|
||||
exec:
|
||||
group: "exec - 执行模块"
|
||||
path: "exec"
|
||||
terminal:
|
||||
group: "terminal - 终端模块"
|
||||
path: "terminal"
|
||||
logging:
|
||||
# 全局日志打印
|
||||
printer:
|
||||
@@ -208,6 +214,7 @@ orion:
|
||||
field:
|
||||
ignore:
|
||||
- password,beforePassword,newPassword,useNewPassword,publicKey,privateKey,privateKeyPassword
|
||||
- accessKey,secretKey
|
||||
- metrics
|
||||
desensitize:
|
||||
storage:
|
||||
|
||||
@@ -39,9 +39,9 @@ import java.util.function.Function;
|
||||
*/
|
||||
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();
|
||||
|
||||
@@ -50,6 +50,7 @@ public class ReplaceVersion {
|
||||
"docker/adminer/build.sh",
|
||||
"docker/mysql/build.sh",
|
||||
"docker/redis/build.sh",
|
||||
"docker/guacd/build.sh",
|
||||
"docker/service/build.sh",
|
||||
"docker/ui/build.sh",
|
||||
"docker-compose.yml",
|
||||
|
||||
Reference in New Issue
Block a user