网站后台数字排版该怎么做,怎样架设网站,网站推广联盟,wordpress 摘要显示一、需求
在系统中使用Web Shell连接集群的登录节点
二、实现
前端使用Vue#xff0c;WebSocket实现前后端通信#xff0c;后端使用JSch ssh通讯包。
1. 前端核心代码
templatediv classshell-containerdiv idshell/WebSocket实现前后端通信后端使用JSch ssh通讯包。
1. 前端核心代码
templatediv classshell-containerdiv idshell//div
/templatescriptimport xterm/css/xterm.css
import { Terminal } from xterm
import { FitAddon } from xterm-addon-fitexport default {name: WebShell,props: {socketURI: {type: String,default: },},watch: {socketURI: {deep: true, //对象内部属性的监听关键。immediate: true,handler() {this.initSocket();},},},data() {return {term: undefined,rows: 24,cols: 80,path: ,isShellConn: false // shell是否连接成功}},mounted() {const { onTerminalResize } this;this.initSocket();// 通过防抖函数const resizedFunc this.debounce(function() {onTerminalResize();}, 250); // 250毫秒内只执行一次 window.addEventListener(resize, resizedFunc);},beforeUnmount() {this.socket.close();this.termthis.term.dispose();window.removeEventListener(resize);},methods: {initTerm() {let term new Terminal({rendererType: canvas, //渲染类型rows: this.rows, //行数cols: this.cols, // 不指定行数自动回车后光标从下一行开始convertEol: true, //启用时光标将设置为下一行的开头disableStdin: false, //是否应禁用输入windowsMode: true, // 根据窗口换行cursorBlink: true, //光标闪烁theme: {foreground: #ECECEC, //字体background: #000000, //背景色cursor: help, //设置光标lineHeight: 20,},});this.term term;const fitAddon new FitAddon();this.term.loadAddon(fitAddon);this.fitAddon fitAddon;let element document.getElementById(shell);term.open(element);// 自适应大小(使终端的尺寸和几何尺寸适合于终端容器的尺寸)初始化的时候宽高都是对的fitAddon.fit();term.focus();//监视命令行输入this.term.onData((data) {let dataWrapper data;if (dataWrapper \r) {dataWrapper \n;} else if (dataWrapper \u0003) {// 输入ctrlcdataWrapper \n;}// 将输入的命令通知给后台后台返回数据。this.socket.send(JSON.stringify({ type: command, data: dataWrapper }));});},onTerminalResize() {this.fitAddon.fit();this.socket.send(JSON.stringify({type: resize,data: {rows: this.term.rows,cols: this.term.cols,}}));},initSocket() {if (this.socketURI ) {return;}// 添加path、cols、rowsconst uri ${this.socketURI}path${this.path}cols${this.cols}rows${this.rows};console.log(uri);this.socket new WebSocket(uri);this.socketOnClose();this.socketOnOpen();this.socketOnmessage();this.socketOnError();},socketOnOpen() {this.socket.onopen () {console.log(websocket链接成功);this.initTerm();};},socketOnmessage() {this.socket.onmessage (evt) {try {if (typeof evt.data string) {const msg JSON.parse(evt.data);switch(msg.type) {case command:// 将返回的数据写入xterm回显在webshell上this.term.write(msg.data);// 当shell首次连接成功时才发送resize事件if (!this.isShellConn) {// when server ready for connection,send resize to serverthis.onTerminalResize();this.isShellConn true;}break;case exit:this.term.write(Process exited with code 0);break;}}} catch (e) {console.error(e);console.log(parse json error., evt.data);}};},socketOnClose() {this.socket.onclose () {this.socket.close();console.log(关闭 socket);window.removeEventListener(resize, this.onTerminalResize);};},socketOnError() {this.socket.onerror () {console.log(socket 链接失败);};},debounce(func, wait) { let timeout; return function() { const context this; const args arguments; clearTimeout(timeout); timeout setTimeout(function() { func.apply(context, args); }, wait); }; } }
}
/script!-- Add scoped attribute to limit CSS to this component only --
style scoped
#shell {width: 100%;height: 100%;
}
.shell-container {height: 100%;
}
/style
2. 后端核心代码
package com.example.webshell.service.impl;import com.alibaba.fastjson.JSONObject;
import com.example.webshell.constant.Constant;
import com.example.webshell.entity.LoginNodeInfo;
import com.example.webshell.entity.ShellConnectInfo;
import com.example.webshell.entity.SocketData;
import com.example.webshell.entity.WebShellParam;
import com.example.webshell.service.WebShellService;
import com.example.webshell.utils.ThreadPoolUtils;
import com.example.webshell.utils.WebShellUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;import static com.example.webshell.constant.Constant.*;Slf4j
Service
public class WebShellServiceImpl implements WebShellService {/*** 存放ssh连接信息的map*/private static final MapString, Object SSH_MAP new ConcurrentHashMap();/*** 初始化连接*/Overridepublic void initConnection(javax.websocket.Session webSocketSession, WebShellParam webShellParam) {JSch jSch new JSch();ShellConnectInfo shellConnectInfo new ShellConnectInfo();shellConnectInfo.setJsch(jSch);shellConnectInfo.setSession(webSocketSession);String uuid WebShellUtil.getUuid(webSocketSession);// 根据集群和登录节点查询IP TODOLoginNodeInfo loginNodeInfo new LoginNodeInfo(demo_admin, demo_admin, 192.168.88.102, 22);//启动线程异步处理ThreadPoolUtils.execute(() - {try {connectToSsh(shellConnectInfo, webShellParam, loginNodeInfo, webSocketSession);} catch (JSchException e) {log.error(web shell连接异常: {}, e.getMessage());sendMessage(webSocketSession, new SocketData(OPERATE_ERROR, e.getMessage()));close(webSocketSession);}});//将这个ssh连接信息放入缓存中SSH_MAP.put(uuid, shellConnectInfo);}/*** 处理客户端发送的数据*/Overridepublic void handleMessage(javax.websocket.Session webSocketSession, String message) {ObjectMapper objectMapper new ObjectMapper();SocketData shellData;try {shellData objectMapper.readValue(message, SocketData.class);String userId WebShellUtil.getUuid(webSocketSession);//找到刚才存储的ssh连接对象ShellConnectInfo shellConnectInfo (ShellConnectInfo) SSH_MAP.get(userId);if (shellConnectInfo ! null) {if (OPERATE_RESIZE.equals(shellData.getType())) {ChannelShell channel shellConnectInfo.getChannel();Object data shellData.getData();Map map objectMapper.readValue(JSONObject.toJSONString(data), Map.class);System.out.println(map);channel.setPtySize(Integer.parseInt(map.get(cols).toString()), Integer.parseInt(map.get(rows).toString()), 0, 0);} else if (OPERATE_COMMAND.equals(shellData.getType())) {String command shellData.getData().toString();sendToTerminal(shellConnectInfo.getChannel(), command);// 退出状态码int exitStatus shellConnectInfo.getChannel().getExitStatus();System.out.println(exitStatus);} else {log.error(不支持的操作);close(webSocketSession);}}} catch (Exception e) {e.printStackTrace();log.error(消息处理异常: {}, e.getMessage());}}/*** 关闭连接*/private void close(javax.websocket.Session webSocketSession) {String userId WebShellUtil.getUuid(webSocketSession);ShellConnectInfo shellConnectInfo (ShellConnectInfo) SSH_MAP.get(userId);if (shellConnectInfo ! null) {//断开连接if (shellConnectInfo.getChannel() ! null) {shellConnectInfo.getChannel().disconnect();}//map中移除SSH_MAP.remove(userId);}}/*** 使用jsch连接终端*/private void connectToSsh(ShellConnectInfo shellConnectInfo, WebShellParam webShellParam, LoginNodeInfo loginNodeInfo, javax.websocket.Session webSocketSession) throws JSchException {Properties config new Properties();// SSH 连接远程主机时会检查主机的公钥。如果是第一次该主机会显示该主机的公钥摘要提示用户是否信任该主机config.put(StrictHostKeyChecking, no);//获取jsch的会话Session session shellConnectInfo.getJsch().getSession(loginNodeInfo.getUsername(), loginNodeInfo.getHost(), loginNodeInfo.getPort());session.setConfig(config);//设置密码session.setPassword(loginNodeInfo.getPassword());//连接超时时间30ssession.connect(30 * 1000);//查询上次登录时间showLastLogin(session, webSocketSession, loginNodeInfo.getUsername());//开启交互式shell通道ChannelShell channel (ChannelShell) session.openChannel(shell);//设置channelshellConnectInfo.setChannel(channel);//通道连接超时时间3schannel.connect(3 * 1000);channel.setPty(true);//读取终端返回的信息流try (InputStream inputStream channel.getInputStream()) {//循环读取byte[] buffer new byte[Constant.BUFFER_SIZE];int i;//如果没有数据来线程会一直阻塞在这个地方等待数据。while ((i inputStream.read(buffer)) ! -1) {sendMessage(webSocketSession, new SocketData(OPERATE_COMMAND, new String(Arrays.copyOfRange(buffer, 0, i))));}} catch (IOException e) {log.error(读取终端返回的信息流异常, e);} finally {//断开连接后关闭会话session.disconnect();channel.disconnect();}}/*** 向前端展示上次登录信息*/private void showLastLogin(Session session, javax.websocket.Session webSocketSession, String username) throws JSchException {ChannelExec channelExec (ChannelExec) session.openChannel(exec);channelExec.setCommand(lastlog -u username);channelExec.connect();channelExec.setErrStream(System.err);try (InputStream inputStream channelExec.getInputStream()) {byte[] buffer new byte[Constant.BUFFER_SIZE];int i;StringBuilder sb new StringBuilder();while ((i inputStream.read(buffer)) ! -1) {sb.append(new String(Arrays.copyOfRange(buffer, 0, i)));}// 解析结果String[] split sb.toString().split(\n);if (split.length 1) {String[] items split[1].split(\\s, 4);String msg String.format(Last login: %s from %s\n, items[3], items[2]);sendMessage(webSocketSession, new SocketData(OPERATE_COMMAND, msg));}} catch (IOException e) {log.error(读取终端返回的信息流异常, e);} finally {channelExec.disconnect();}}/*** 数据写回前端*/private void sendMessage(javax.websocket.Session webSocketSession, SocketData data) {try {webSocketSession.getBasicRemote().sendText(JSONObject.toJSONString(data));} catch (IOException e) {log.error(数据写回前端异常, e);}}/*** 将消息转发到终端*/private void sendToTerminal(Channel channel, String command) {if (channel ! null) {try {OutputStream outputStream channel.getOutputStream();outputStream.write(command.getBytes());outputStream.flush();} catch (IOException e) {log.error(web shell将消息转发到终端异常:{}, e.getMessage());}}}
}
三、效果展示