修改 token 置换逻辑.

This commit is contained in:
lijiahang
2024-01-04 16:00:51 +08:00
parent 71299801c5
commit acc165b4c6
23 changed files with 236 additions and 142 deletions

View File

@@ -71,6 +71,8 @@ public interface ErrorMessage {
String ANY_NO_PERMISSION = "{}无权限"; String ANY_NO_PERMISSION = "{}无权限";
String SESSION_PRESENT = "会话已存在";
String SESSION_ABSENT = "会话不存在"; String SESSION_ABSENT = "会话不存在";
} }

View File

@@ -13,8 +13,6 @@ public interface ExtraFieldConst extends FieldConst {
String TRACE_ID = "traceId"; String TRACE_ID = "traceId";
String SESSION_ID = "sessionId";
String IDENTITY = "identity"; String IDENTITY = "identity";
String GROUP_NAME = "groupName"; String GROUP_NAME = "groupName";
@@ -33,4 +31,8 @@ public interface ExtraFieldConst extends FieldConst {
String GRANT_NAME = "grantName"; String GRANT_NAME = "grantName";
String CHANNEL_ID = "channelId";
String SESSION_ID = "sessionId";
} }

View File

@@ -31,7 +31,7 @@ public class AssetWebSocketConfiguration implements WebSocketConfigurer {
@Override @Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 终端 // 终端
registry.addHandler(terminalMessageDispatcher, prefix + "/host/terminal/{token}") registry.addHandler(terminalMessageDispatcher, prefix + "/host/terminal/{accessToken}")
.addInterceptors(terminalAccessInterceptor) .addInterceptors(terminalAccessInterceptor)
.setAllowedOrigins("*"); .setAllowedOrigins("*");
} }

View File

@@ -1,7 +1,7 @@
package com.orion.ops.module.asset.controller; package com.orion.ops.module.asset.controller;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
import com.orion.ops.framework.web.core.annotation.RestWrapper; import com.orion.ops.framework.web.core.annotation.RestWrapper;
import com.orion.ops.module.asset.entity.vo.HostTerminalAccessVO;
import com.orion.ops.module.asset.service.HostTerminalService; import com.orion.ops.module.asset.service.HostTerminalService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@@ -36,8 +36,8 @@ public class HostTerminalController {
@GetMapping("/access") @GetMapping("/access")
@Operation(summary = "获取主机终端 accessToken") @Operation(summary = "获取主机终端 accessToken")
@PreAuthorize("@ss.hasPermission('asset:host-terminal:access')") @PreAuthorize("@ss.hasPermission('asset:host-terminal:access')")
public String getHostTerminalAccessToken() { public HostTerminalAccessVO getHostTerminalAccessToken() {
return hostTerminalService.getHostTerminalAccessToken(SecurityUtils.getLoginUserId()); return hostTerminalService.getHostTerminalAccessToken();
} }
} }

View File

@@ -25,4 +25,7 @@ public class HostTerminalAccessDTO {
@Schema(description = "userId") @Schema(description = "userId")
private Long userId; private Long userId;
@Schema(description = "username")
private String username;
} }

View File

@@ -0,0 +1,33 @@
package com.orion.ops.module.asset.entity.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;
/**
* 主机终端访问 响应对象
*
* @author Jiahang Li
* @version 1.0.0
* @since 2024/1/4 15:42
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "HostTerminalAccessVO", description = "主机终端访问 响应对象")
public class HostTerminalAccessVO implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "accessToken")
private String accessToken;
@Schema(description = "session 起始量")
private String sessionInitial;
}

View File

@@ -20,7 +20,7 @@ import javax.annotation.PostConstruct;
public enum InputTypeEnum { public enum InputTypeEnum {
/** /**
* 主机连接检查 置换token / 检查权限 * 主机连接检查
*/ */
CHECK("ck", CHECK("ck",
TerminalCheckHandler.class, TerminalCheckHandler.class,
@@ -106,7 +106,7 @@ public enum InputTypeEnum {
return null; return null;
} }
for (InputTypeEnum value : values()) { for (InputTypeEnum value : values()) {
if (payload.startsWith(value.type + SEPARATOR) || value.type.equals(payload)) { if (payload.startsWith(value.type + SEPARATOR) || payload.equals(value.type)) {
return value; return value;
} }
} }

View File

@@ -18,7 +18,7 @@ public enum OutputTypeEnum {
/** /**
* 主机连接检查 * 主机连接检查
*/ */
CHECK("ck", "${type}|${session}|${token}|${result}|${errorMessage}"), CHECK("ck", "${type}|${session}|${result}|${errorMessage}"),
/** /**
* 主机连接 * 主机连接

View File

@@ -17,38 +17,38 @@ public abstract class AbstractTerminalHandler<T extends TerminalBasePayload> imp
/** /**
* 发送消息 * 发送消息
* *
* @param session session * @param channel channel
* @param type type * @param type type
* @param body body * @param body body
* @param <E> E * @param <E> E
*/ */
public <E extends TerminalBasePayload> void send(WebSocketSession session, OutputTypeEnum type, E body) { public <E extends TerminalBasePayload> void send(WebSocketSession channel, OutputTypeEnum type, E body) {
body.setType(type.getType()); body.setType(type.getType());
// 发送消息 // 发送消息
this.send(session, type.format(body)); this.send(channel, type.format(body));
} }
/** /**
* 发送消息 * 发送消息
* *
* @param session session * @param channel channel
* @param message message * @param message message
*/ */
protected void send(WebSocketSession session, String message) { protected void send(WebSocketSession channel, String message) {
WebSockets.sendText(session, message); WebSockets.sendText(channel, message);
} }
/** /**
* 获取属性 * 获取属性
* *
* @param session session * @param channel channel
* @param attr attr * @param attr attr
* @param <E> T * @param <E> T
* @return T * @return T
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected <E> E getAttr(WebSocketSession session, String attr) { protected <E> E getAttr(WebSocketSession channel, String attr) {
return (E) session.getAttributes().get(attr); return (E) channel.getAttributes().get(attr);
} }
} }

View File

@@ -15,9 +15,9 @@ public interface ITerminalHandler<T extends TerminalBasePayload> {
/** /**
* 处理消息 * 处理消息
* *
* @param session session * @param channel channel
* @param payload payload * @param payload payload
*/ */
void handle(WebSocketSession session, T payload); void handle(WebSocketSession channel, T payload);
} }

View File

@@ -1,6 +1,5 @@
package com.orion.ops.module.asset.handler.host.terminal.handler; package com.orion.ops.module.asset.handler.host.terminal.handler;
import com.orion.lang.id.UUIds;
import com.orion.lang.utils.collect.Maps; import com.orion.lang.utils.collect.Maps;
import com.orion.ops.framework.biz.operator.log.core.service.OperatorLogFrameworkService; import com.orion.ops.framework.biz.operator.log.core.service.OperatorLogFrameworkService;
import com.orion.ops.framework.biz.operator.log.core.uitls.OperatorLogFiller; import com.orion.ops.framework.biz.operator.log.core.uitls.OperatorLogFiller;
@@ -16,8 +15,10 @@ import com.orion.ops.module.asset.entity.request.host.HostConnectLogCreateReques
import com.orion.ops.module.asset.enums.HostConnectStatusEnum; import com.orion.ops.module.asset.enums.HostConnectStatusEnum;
import com.orion.ops.module.asset.enums.HostConnectTypeEnum; import com.orion.ops.module.asset.enums.HostConnectTypeEnum;
import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum; import com.orion.ops.module.asset.handler.host.terminal.enums.OutputTypeEnum;
import com.orion.ops.module.asset.handler.host.terminal.manager.TerminalManager;
import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalCheckRequest; import com.orion.ops.module.asset.handler.host.terminal.model.request.TerminalCheckRequest;
import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalCheckResponse; import com.orion.ops.module.asset.handler.host.terminal.model.response.TerminalCheckResponse;
import com.orion.ops.module.asset.handler.host.terminal.session.ITerminalSession;
import com.orion.ops.module.asset.service.HostConnectLogService; import com.orion.ops.module.asset.service.HostConnectLogService;
import com.orion.ops.module.asset.service.HostTerminalService; import com.orion.ops.module.asset.service.HostTerminalService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -50,26 +51,25 @@ public class TerminalCheckHandler extends AbstractTerminalHandler<TerminalCheckR
@Resource @Resource
private OperatorLogFrameworkService operatorLogFrameworkService; private OperatorLogFrameworkService operatorLogFrameworkService;
@Resource
private TerminalManager terminalManager;
@Override @Override
public void handle(WebSocketSession session, TerminalCheckRequest payload) { public void handle(WebSocketSession channel, TerminalCheckRequest payload) {
Long hostId = payload.getHostId(); Long hostId = payload.getHostId();
Long userId = this.getAttr(session, ExtraFieldConst.USER_ID); Long userId = this.getAttr(channel, ExtraFieldConst.USER_ID);
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
String token = UUIds.random15(); String sessionId = payload.getSession();
log.info("TerminalCheckHandler-handle start userId: {}, hostId: {}, token: {}", userId, hostId, token); log.info("TerminalCheckHandler-handle start userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId);
// 查询主机信息 // 检查 session 是否存在
HostDO host = hostDAO.selectById(hostId); if (this.checkSession(channel, payload)) {
// 不存在返回错误信息 log.info("TerminalCheckHandler-handle present session userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId);
return;
}
// 获取主机信息
HostDO host = this.checkHost(channel, payload, hostId);
if (host == null) { if (host == null) {
log.info("TerminalCheckHandler-handle unknown host userId: {}, hostId: {}", userId, hostId); log.info("TerminalCheckHandler-handle unknown host userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId);
this.send(session,
OutputTypeEnum.CHECK,
TerminalCheckResponse.builder()
.session(payload.getSession())
.token(token)
.result(BooleanBit.FALSE.getValue())
.errorMessage(ErrorMessage.HOST_ABSENT)
.build());
return; return;
} }
Exception ex = null; Exception ex = null;
@@ -77,58 +77,108 @@ public class TerminalCheckHandler extends AbstractTerminalHandler<TerminalCheckR
// 获取连接信息 // 获取连接信息
HostTerminalConnectDTO connect = hostTerminalService.getTerminalConnectInfo(userId, host); HostTerminalConnectDTO connect = hostTerminalService.getTerminalConnectInfo(userId, host);
// 设置到缓存中 // 设置到缓存中
session.getAttributes().put(token, connect); channel.getAttributes().put(sessionId, connect);
log.info("TerminalCheckHandler-handle success userId: {}, hostId: {}, token: {}", userId, hostId, token); log.info("TerminalCheckHandler-handle success userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId);
} catch (Exception e) { } catch (Exception e) {
ex = e; ex = e;
log.error("TerminalCheckHandler-handle error userId: {}, hostId: {}, token: {}", userId, hostId, token, e); log.error("TerminalCheckHandler-handle error userId: {}, hostId: {}, sessionId: {}", userId, hostId, sessionId, e);
} }
// 记录主机日志 // 记录主机日志
this.saveTerminalLog(session, userId, host, startTime, ex, token); this.saveTerminalLog(channel, userId, host, startTime, ex, sessionId);
// 响应检查结果 // 响应检查结果
this.send(session, this.send(channel,
OutputTypeEnum.CHECK, OutputTypeEnum.CHECK,
TerminalCheckResponse.builder() TerminalCheckResponse.builder()
.session(payload.getSession()) .session(payload.getSession())
.token(token)
.result(BooleanBit.of(ex == null).getValue()) .result(BooleanBit.of(ex == null).getValue())
.errorMessage(ex == null ? null : ex.getMessage()) .errorMessage(ex == null ? null : ex.getMessage())
.build()); .build());
} }
/**
* 检查会话是否存在
*
* @param channel channel
* @param payload payload
* @return 是否存在
*/
private boolean checkSession(WebSocketSession channel, TerminalCheckRequest payload) {
ITerminalSession terminalSession = terminalManager.getSession(channel.getId(), payload.getSession());
if (terminalSession != null) {
this.sendCheckFailedMessage(channel, payload, ErrorMessage.SESSION_PRESENT);
return true;
}
return false;
}
/**
* 获取主机信息
*
* @param channel channel
* @param payload payload
* @param hostId hostId
* @return host
*/
private HostDO checkHost(WebSocketSession channel, TerminalCheckRequest payload, Long hostId) {
// 查询主机信息
HostDO host = hostDAO.selectById(hostId);
// 不存在返回错误信息
if (host == null) {
this.sendCheckFailedMessage(channel, payload, ErrorMessage.HOST_ABSENT);
}
return host;
}
/**
* 发送检查失败消息
*
* @param channel channel
* @param payload payload
* @param msg msg
*/
private void sendCheckFailedMessage(WebSocketSession channel, TerminalCheckRequest payload, String msg) {
TerminalCheckResponse build = TerminalCheckResponse.builder()
.session(payload.getSession())
.result(BooleanBit.FALSE.getValue())
.errorMessage(msg)
.build();
// 发送
this.send(channel, OutputTypeEnum.CHECK, build);
}
/** /**
* 记录主机日志 * 记录主机日志
* *
* @param session session * @param channel channel
* @param userId userId * @param userId userId
* @param host host * @param host host
* @param startTime startTime * @param startTime startTime
* @param ex ex * @param ex ex
* @param terminalToken terminalToken * @param sessionId sessionId
*/ */
private void saveTerminalLog(WebSocketSession session, private void saveTerminalLog(WebSocketSession channel,
Long userId, Long userId,
HostDO host, HostDO host,
long startTime, long startTime,
Exception ex, Exception ex,
String terminalToken) { String sessionId) {
Long hostId = host.getId(); Long hostId = host.getId();
String hostName = host.getName(); String hostName = host.getName();
String username = this.getAttr(session, ExtraFieldConst.USERNAME); String username = this.getAttr(channel, ExtraFieldConst.USERNAME);
// 额外参数 // 额外参数
Map<String, Object> extra = Maps.newMap(); Map<String, Object> extra = Maps.newMap();
extra.put(OperatorLogs.ID, hostId); extra.put(OperatorLogs.ID, hostId);
extra.put(OperatorLogs.NAME, hostName); extra.put(OperatorLogs.NAME, hostName);
extra.put(OperatorLogs.TOKEN, terminalToken); extra.put(OperatorLogs.CHANNEL_ID, channel.getId());
extra.put(OperatorLogs.SESSION_ID, session.getId()); extra.put(OperatorLogs.SESSION_ID, sessionId);
// 日志参数 // 日志参数
OperatorLogFiller logModel = OperatorLogFiller.create() OperatorLogFiller logModel = OperatorLogFiller.create()
// 填充用户信息 // 填充用户信息
.fillUserInfo(userId, username) .fillUserInfo(userId, username)
// 填充 traceId // 填充 traceId
.fillTraceId(this.getAttr(session, ExtraFieldConst.TRACE_ID)) .fillTraceId(this.getAttr(channel, ExtraFieldConst.TRACE_ID))
// 填充请求留痕信息 // 填充请求留痕信息
.fillIdentity(this.getAttr(session, ExtraFieldConst.IDENTITY)) .fillIdentity(this.getAttr(channel, ExtraFieldConst.IDENTITY))
// 填充使用时间 // 填充使用时间
.fillUsedTime(startTime) .fillUsedTime(startTime)
// 填充结果信息 // 填充结果信息
@@ -147,7 +197,7 @@ public class TerminalCheckHandler extends AbstractTerminalHandler<TerminalCheckR
.hostName(hostName) .hostName(hostName)
.hostAddress(host.getAddress()) .hostAddress(host.getAddress())
.status(ex == null ? HostConnectStatusEnum.CONNECTING.name() : HostConnectStatusEnum.FAILED.name()) .status(ex == null ? HostConnectStatusEnum.CONNECTING.name() : HostConnectStatusEnum.FAILED.name())
.token(terminalToken) .token(sessionId)
.extra(extra) .extra(extra)
.build(); .build();
hostConnectLogService.create(HostConnectTypeEnum.SSH, connectLog); hostConnectLogService.create(HostConnectTypeEnum.SSH, connectLog);

View File

@@ -23,9 +23,10 @@ public class TerminalCloseHandler extends AbstractTerminalHandler<TerminalBasePa
private TerminalManager terminalManager; private TerminalManager terminalManager;
@Override @Override
public void handle(WebSocketSession session, TerminalBasePayload payload) { public void handle(WebSocketSession channel, TerminalBasePayload payload) {
log.info("TerminalCloseHandler-handle start session: {}", payload.getSession());
// 关闭会话 // 关闭会话
terminalManager.closeSession(session.getId(), payload.getSession()); terminalManager.closeSession(channel.getId(), payload.getSession());
} }
} }

View File

@@ -47,14 +47,14 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
private TerminalManager terminalManager; private TerminalManager terminalManager;
@Override @Override
public void handle(WebSocketSession session, TerminalConnectRequest payload) { public void handle(WebSocketSession channel, TerminalConnectRequest payload) {
String token = payload.getSession(); String sessionId = payload.getSession();
log.info("TerminalConnectHandler-handle start token: {}", token); log.info("TerminalConnectHandler-handle start sessionId: {}", sessionId);
// 获取主机连接信息 // 获取主机连接信息
HostTerminalConnectDTO connect = this.getAttr(session, token); HostTerminalConnectDTO connect = this.getAttr(channel, sessionId);
if (connect == null) { if (connect == null) {
log.info("TerminalConnectHandler-handle unknown token: {}", token); log.info("TerminalConnectHandler-handle unknown sessionId: {}", sessionId);
this.send(session, this.send(channel,
OutputTypeEnum.CONNECT, OutputTypeEnum.CONNECT,
TerminalConnectResponse.builder() TerminalConnectResponse.builder()
.session(payload.getSession()) .session(payload.getSession())
@@ -64,20 +64,20 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
return; return;
} }
// 移除会话连接信息 // 移除会话连接信息
session.getAttributes().remove(token); channel.getAttributes().remove(sessionId);
Exception ex = null; Exception ex = null;
try { try {
// 连接主机 // 连接主机
TerminalSession terminalSession = this.connect(token, connect, session, payload); TerminalSession terminalSession = this.connect(sessionId, connect, channel, payload);
// 添加会话到 manager // 添加会话到 manager
terminalManager.addSession(terminalSession); terminalManager.addSession(terminalSession);
} catch (Exception e) { } catch (Exception e) {
ex = e; ex = e;
// 修改连接状态为失败 // 修改连接状态为失败
hostConnectLogService.updateStatusByToken(token, HostConnectStatusEnum.FAILED); hostConnectLogService.updateStatusByToken(sessionId, HostConnectStatusEnum.FAILED);
} }
// 返回连接状态 // 返回连接状态
this.send(session, this.send(channel,
OutputTypeEnum.CONNECT, OutputTypeEnum.CONNECT,
TerminalConnectResponse.builder() TerminalConnectResponse.builder()
.session(payload.getSession()) .session(payload.getSession())
@@ -89,15 +89,15 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
/** /**
* 连接主机 * 连接主机
* *
* @param token token * @param sessionId sessionId
* @param connect connect * @param connect connect
* @param session session * @param channel channel
* @param body body * @param body body
* @return session * @return channel
*/ */
private TerminalSession connect(String token, private TerminalSession connect(String sessionId,
HostTerminalConnectDTO connect, HostTerminalConnectDTO connect,
WebSocketSession session, WebSocketSession channel,
TerminalConnectRequest body) { TerminalConnectRequest body) {
TerminalSession terminalSession = null; TerminalSession terminalSession = null;
try { try {
@@ -109,13 +109,13 @@ public class TerminalConnectHandler extends AbstractTerminalHandler<TerminalConn
.build(); .build();
// 建立连接 // 建立连接
SessionStore sessionStore = hostTerminalService.openSessionStore(connect); SessionStore sessionStore = hostTerminalService.openSessionStore(connect);
terminalSession = new TerminalSession(token, session, sessionStore, config); terminalSession = new TerminalSession(sessionId, channel, sessionStore, config);
terminalSession.connect(body.getCols(), body.getRows()); terminalSession.connect(body.getCols(), body.getRows());
log.info("TerminalConnectHandler-handle success token: {}", token); log.info("TerminalConnectHandler-handle success sessionId: {}", sessionId);
return terminalSession; return terminalSession;
} catch (Exception e) { } catch (Exception e) {
Streams.close(terminalSession); Streams.close(terminalSession);
log.error("TerminalConnectHandler-handle error token: {}", token, e); log.error("TerminalConnectHandler-handle error sessionId: {}", sessionId, e);
throw e; throw e;
} }
} }

View File

@@ -24,9 +24,9 @@ public class TerminalExecHandler extends AbstractTerminalHandler<TerminalExecReq
private TerminalManager terminalManager; private TerminalManager terminalManager;
@Override @Override
public void handle(WebSocketSession session, TerminalExecRequest payload) { public void handle(WebSocketSession channel, TerminalExecRequest payload) {
// 获取会话 // 获取会话
ITerminalSession terminalSession = terminalManager.getSession(session.getId(), payload.getSession()); ITerminalSession terminalSession = terminalManager.getSession(channel.getId(), payload.getSession());
if (terminalSession != null) { if (terminalSession != null) {
// 执行命令 // 执行命令
terminalSession.write(payload.getCommand()); terminalSession.write(payload.getCommand());

View File

@@ -24,9 +24,9 @@ public class TerminalInputHandler extends AbstractTerminalHandler<TerminalInputR
private TerminalManager terminalManager; private TerminalManager terminalManager;
@Override @Override
public void handle(WebSocketSession session, TerminalInputRequest payload) { public void handle(WebSocketSession channel, TerminalInputRequest payload) {
// 获取会话 // 获取会话
ITerminalSession terminalSession = terminalManager.getSession(session.getId(), payload.getSession()); ITerminalSession terminalSession = terminalManager.getSession(channel.getId(), payload.getSession());
if (terminalSession != null) { if (terminalSession != null) {
// 处理输入 // 处理输入
terminalSession.write(payload.getCommand()); terminalSession.write(payload.getCommand());

View File

@@ -18,9 +18,9 @@ import org.springframework.web.socket.WebSocketSession;
public class TerminalPingHandler extends AbstractTerminalHandler<TerminalBasePayload> { public class TerminalPingHandler extends AbstractTerminalHandler<TerminalBasePayload> {
@Override @Override
public void handle(WebSocketSession session, TerminalBasePayload payload) { public void handle(WebSocketSession channel, TerminalBasePayload payload) {
// 发送 pong // 发送 pong
this.send(session, OutputTypeEnum.PONG.getType()); this.send(channel, OutputTypeEnum.PONG.getType());
} }
} }

View File

@@ -24,9 +24,9 @@ public class TerminalResizeHandler extends AbstractTerminalHandler<TerminalResiz
private TerminalManager terminalManager; private TerminalManager terminalManager;
@Override @Override
public void handle(WebSocketSession session, TerminalResizeRequest payload) { public void handle(WebSocketSession channel, TerminalResizeRequest payload) {
// 获取会话 // 获取会话
ITerminalSession terminalSession = terminalManager.getSession(session.getId(), payload.getSession()); ITerminalSession terminalSession = terminalManager.getSession(channel.getId(), payload.getSession());
if (terminalSession != null) { if (terminalSession != null) {
// 修改大小 // 修改大小
terminalSession.resize(payload.getCols(), payload.getRows()); terminalSession.resize(payload.getCols(), payload.getRows());

View File

@@ -22,37 +22,37 @@ public class TerminalManager {
/** /**
* 会话存储器 * 会话存储器
*/ */
private final MultiConcurrentHashMap<String, String, ITerminalSession> sessions = MultiConcurrentHashMap.create(); private final MultiConcurrentHashMap<String, String, ITerminalSession> channelSessions = MultiConcurrentHashMap.create();
/** /**
* 添加会话 * 添加会话
* *
* @param terminalSession terminalSession * @param session session
*/ */
public void addSession(TerminalSession terminalSession) { public void addSession(TerminalSession session) {
sessions.put(terminalSession.getSession().getId(), terminalSession.getToken(), terminalSession); channelSessions.put(session.getChannel().getId(), session.getSessionId(), session);
} }
/** /**
* 获取会话 * 获取会话
* *
* @param id id * @param channelId channelId
* @param token token * @param sessionId sessionId
* @return session * @return session
*/ */
public ITerminalSession getSession(String id, String token) { public ITerminalSession getSession(String channelId, String sessionId) {
return sessions.get(id, token); return channelSessions.get(channelId, sessionId);
} }
/** /**
* 关闭会话 * 关闭会话
* *
* @param id id * @param channelId channelId
* @param token token * @param sessionId sessionId
*/ */
public void closeSession(String id, String token) { public void closeSession(String channelId, String sessionId) {
// 获取并移除 // 获取并移除
ITerminalSession session = sessions.removeElement(id, token); ITerminalSession session = channelSessions.removeElement(channelId, sessionId);
if (session != null) { if (session != null) {
Streams.close(session); Streams.close(session);
} }
@@ -61,11 +61,11 @@ public class TerminalManager {
/** /**
* 关闭全部会话 * 关闭全部会话
* *
* @param id id * @param channelId channelId
*/ */
public void closeAll(String id) { public void closeAll(String channelId) {
// 获取并移除 // 获取并移除
ConcurrentHashMap<String, ITerminalSession> session = sessions.remove(id); ConcurrentHashMap<String, ITerminalSession> session = channelSessions.remove(channelId);
if (Maps.isEmpty(session)) { if (Maps.isEmpty(session)) {
session.values().forEach(Streams::close); session.values().forEach(Streams::close);
} }

View File

@@ -23,9 +23,6 @@ import lombok.experimental.SuperBuilder;
@Schema(name = "TerminalCheckResponse", description = "主机连接检查响应 实体对象") @Schema(name = "TerminalCheckResponse", description = "主机连接检查响应 实体对象")
public class TerminalCheckResponse extends TerminalBasePayload { public class TerminalCheckResponse extends TerminalBasePayload {
@Schema(description = "token")
private String token;
@Schema(description = "检查结果") @Schema(description = "检查结果")
private Integer result; private Integer result;

View File

@@ -32,10 +32,10 @@ import java.io.InputStream;
public class TerminalSession implements ITerminalSession { public class TerminalSession implements ITerminalSession {
@Getter @Getter
private final String token; private final String sessionId;
@Getter @Getter
private final WebSocketSession session; private final WebSocketSession channel;
private final TerminalConfig config; private final TerminalConfig config;
@@ -48,12 +48,12 @@ public class TerminalSession implements ITerminalSession {
private volatile boolean close; private volatile boolean close;
public TerminalSession(String token, public TerminalSession(String sessionId,
WebSocketSession session, WebSocketSession channel,
SessionStore sessionStore, SessionStore sessionStore,
TerminalConfig config) { TerminalConfig config) {
this.token = token; this.sessionId = sessionId;
this.session = session; this.channel = channel;
this.sessionStore = sessionStore; this.sessionStore = sessionStore;
this.config = config; this.config = config;
} }
@@ -104,10 +104,10 @@ public class TerminalSession implements ITerminalSession {
Streams.close(executor); Streams.close(executor);
Streams.close(sessionStore); Streams.close(sessionStore);
} catch (Exception e) { } catch (Exception e) {
log.error("terminal 断开连接 失败 token: {}", token, e); log.error("terminal 断开连接失败 {}", sessionId, e);
} }
// 修改状态 // 修改状态
SpringHolder.getBean(HostConnectLogService.class).updateStatusByToken(token, HostConnectStatusEnum.COMPLETE); SpringHolder.getBean(HostConnectLogService.class).updateStatusByToken(sessionId, HostConnectStatusEnum.COMPLETE);
} }
/** /**
@@ -120,22 +120,22 @@ public class TerminalSession implements ITerminalSession {
BufferedInputStream in = new BufferedInputStream(inputStream, Const.BUFFER_KB_4); BufferedInputStream in = new BufferedInputStream(inputStream, Const.BUFFER_KB_4);
int read; int read;
try { try {
while (session.isOpen() && (read = in.read(bs)) != -1) { while (channel.isOpen() && (read = in.read(bs)) != -1) {
String body = lastLine = new String(bs, 0, read, config.getCharset()); String body = lastLine = new String(bs, 0, read, config.getCharset());
// 响应 // 响应
TerminalOutputResponse resp = TerminalOutputResponse.builder() TerminalOutputResponse resp = TerminalOutputResponse.builder()
.session(token) .session(sessionId)
.type(OutputTypeEnum.OUTPUT.getType()) .type(OutputTypeEnum.OUTPUT.getType())
.body(body) .body(body)
.build(); .build();
WebSockets.sendText(session, OutputTypeEnum.OUTPUT.format(resp)); WebSockets.sendText(channel, OutputTypeEnum.OUTPUT.format(resp));
} }
} catch (IOException ex) { } catch (IOException ex) {
log.error("terminal 读取流失败", ex); log.error("terminal 读取流失败", ex);
} }
// eof // eof
if (close) { if (close) {
log.info("terminal eof回调 {}", token); log.info("terminal eof回调 {}", sessionId);
} }
} }

View File

@@ -4,6 +4,7 @@ import com.orion.lang.utils.Urls;
import com.orion.ops.framework.common.constant.ExtraFieldConst; import com.orion.ops.framework.common.constant.ExtraFieldConst;
import com.orion.ops.framework.common.meta.TraceIdHolder; import com.orion.ops.framework.common.meta.TraceIdHolder;
import com.orion.ops.framework.common.utils.Requests; import com.orion.ops.framework.common.utils.Requests;
import com.orion.ops.module.asset.entity.dto.HostTerminalAccessDTO;
import com.orion.ops.module.asset.service.HostTerminalService; import com.orion.ops.module.asset.service.HostTerminalService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpRequest;
@@ -31,24 +32,20 @@ public class TerminalAccessInterceptor implements HandshakeInterceptor {
@Override @Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception { public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
// 获取 token // 获取 accessToken
String token = Urls.getUrlSource(request.getURI().getPath()); String accessToken = Urls.getUrlSource(request.getURI().getPath());
log.info("TerminalInterceptor-beforeHandshake start token: {}", token); log.info("TerminalInterceptor-beforeHandshake start accessToken: {}", accessToken);
attributes.put(ExtraFieldConst.USER_ID, 1L); // 获取连接数据
attributes.put(ExtraFieldConst.USERNAME, "1"); HostTerminalAccessDTO access = hostTerminalService.getAccessInfoByToken(accessToken);
if (access == null) {
log.error("TerminalInterceptor-beforeHandshake absent accessToken: {}", accessToken);
return false;
}
// 设置参数
attributes.put(ExtraFieldConst.USER_ID, access.getUserId());
attributes.put(ExtraFieldConst.USERNAME, access.getUsername());
attributes.put(ExtraFieldConst.TRACE_ID, TraceIdHolder.get()); attributes.put(ExtraFieldConst.TRACE_ID, TraceIdHolder.get());
attributes.put(ExtraFieldConst.IDENTITY, Requests.getIdentity()); attributes.put(ExtraFieldConst.IDENTITY, Requests.getIdentity());
// 获取连接数据
// HostTerminalAccessDTO access = hostTerminalService.getAccessInfoByToken(token);
// if (access == null) {
// log.error("TerminalInterceptor-beforeHandshake absent token: {}", token);
// return false;
// }
// // 设置参数
// attributes.put(ExtraFieldConst.USER_ID, access.getUserId());
// attributes.put(ExtraFieldConst.USERNAME, access.getUsername());
// attributes.put(ExtraFieldConst.TRACE_ID, TraceIdHolder.get());
// attributes.put(ExtraFieldConst.IDENTITY, Requests.getIdentity());
return true; return true;
} }

View File

@@ -4,6 +4,7 @@ import com.orion.net.host.SessionStore;
import com.orion.ops.module.asset.entity.domain.HostDO; import com.orion.ops.module.asset.entity.domain.HostDO;
import com.orion.ops.module.asset.entity.dto.HostTerminalAccessDTO; import com.orion.ops.module.asset.entity.dto.HostTerminalAccessDTO;
import com.orion.ops.module.asset.entity.dto.HostTerminalConnectDTO; import com.orion.ops.module.asset.entity.dto.HostTerminalConnectDTO;
import com.orion.ops.module.asset.entity.vo.HostTerminalAccessVO;
/** /**
* 主机终端服务 * 主机终端服务
@@ -17,10 +18,9 @@ public interface HostTerminalService {
/** /**
* 获取主机终端访问 accessToken * 获取主机终端访问 accessToken
* *
* @param userId userId
* @return session * @return session
*/ */
String getHostTerminalAccessToken(Long userId); HostTerminalAccessVO getHostTerminalAccessToken();
/** /**
* 通过 accessToken 获取主机终端访问信息 * 通过 accessToken 获取主机终端访问信息

View File

@@ -8,9 +8,11 @@ import com.orion.net.host.SessionHolder;
import com.orion.net.host.SessionStore; import com.orion.net.host.SessionStore;
import com.orion.ops.framework.common.constant.Const; import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.constant.ErrorMessage; import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.common.security.LoginUser;
import com.orion.ops.framework.common.utils.CryptoUtils; import com.orion.ops.framework.common.utils.CryptoUtils;
import com.orion.ops.framework.common.utils.Valid; import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.redis.core.utils.RedisStrings; import com.orion.ops.framework.redis.core.utils.RedisStrings;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
import com.orion.ops.module.asset.dao.HostDAO; import com.orion.ops.module.asset.dao.HostDAO;
import com.orion.ops.module.asset.dao.HostIdentityDAO; import com.orion.ops.module.asset.dao.HostIdentityDAO;
import com.orion.ops.module.asset.dao.HostKeyDAO; import com.orion.ops.module.asset.dao.HostKeyDAO;
@@ -20,6 +22,7 @@ import com.orion.ops.module.asset.entity.domain.HostIdentityDO;
import com.orion.ops.module.asset.entity.domain.HostKeyDO; import com.orion.ops.module.asset.entity.domain.HostKeyDO;
import com.orion.ops.module.asset.entity.dto.HostTerminalAccessDTO; import com.orion.ops.module.asset.entity.dto.HostTerminalAccessDTO;
import com.orion.ops.module.asset.entity.dto.HostTerminalConnectDTO; import com.orion.ops.module.asset.entity.dto.HostTerminalConnectDTO;
import com.orion.ops.module.asset.entity.vo.HostTerminalAccessVO;
import com.orion.ops.module.asset.enums.HostConfigTypeEnum; import com.orion.ops.module.asset.enums.HostConfigTypeEnum;
import com.orion.ops.module.asset.enums.HostExtraItemEnum; import com.orion.ops.module.asset.enums.HostExtraItemEnum;
import com.orion.ops.module.asset.enums.HostExtraSshAuthTypeEnum; import com.orion.ops.module.asset.enums.HostExtraSshAuthTypeEnum;
@@ -80,16 +83,22 @@ public class HostTerminalServiceImpl implements HostTerminalService {
private SystemUserApi systemUserApi; private SystemUserApi systemUserApi;
@Override @Override
public String getHostTerminalAccessToken(Long userId) { public HostTerminalAccessVO getHostTerminalAccessToken() {
log.info("HostConnectService.getHostAccessToken userId: {}", userId); LoginUser user = SecurityUtils.getLoginUser();
String token = UUIds.random32(); log.info("HostConnectService.getHostAccessToken userId: {}", user.getId());
String accessToken = UUIds.random19();
HostTerminalAccessDTO access = HostTerminalAccessDTO.builder() HostTerminalAccessDTO access = HostTerminalAccessDTO.builder()
.userId(userId) .userId(user.getId())
.username(user.getUsername())
.build(); .build();
// 设置缓存 // 设置 access 缓存
String key = HostTerminalCacheKeyDefine.HOST_TERMINAL_ACCESS.format(token); String key = HostTerminalCacheKeyDefine.HOST_TERMINAL_ACCESS.format(accessToken);
RedisStrings.setJson(key, HostTerminalCacheKeyDefine.HOST_TERMINAL_ACCESS, access); RedisStrings.setJson(key, HostTerminalCacheKeyDefine.HOST_TERMINAL_ACCESS, access);
return token; return HostTerminalAccessVO.builder()
.accessToken(accessToken)
// 32 进制的 uuid 作为起始量
.sessionInitial(Long.toString(UUIds.random15Long(), 32))
.build();
} }
@Override @Override