最近由于项目需要,要在web端实现类似secureCRT的功能。首先想到在Github上看下有没有现成的轮子,找了一圈没看到合适的。于是结合着开源的资料,实现了一套适合本地项目的基于Springboot和Vue前后端分离版本的网页端SSH。
项目中前端使用vue,由于vue无法直接发起SSH通信,所以还需要一个后端来接受并发起SSH请求,后端自然就选择开发效率很高的Springboot框架了。
我们来分析一下需求。首先需要有个交互界面,模拟terminal终端;其次还需要前后端通讯,需要支持前端发命令到后端以及后端将请求结果发送回前端;最后在后端还需要能处理SSH命令的逻辑。基于以上三点需求,最终的技术选型就是Xterm.js+ Websocket + Jsch组合。

Xterm是一个使用TypeScript编写的前端终端组件,可以直接在浏览器中实现一个命令行终端应用。

WebSocket是一种在单个TCP连接上进行全双工通信的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

Jsch即JavaSecureChannel,Jsch是SSH2的一个纯Java实现。它允许你连接到一个sshd服务器,使用端口转发,X11转发,文件传输等等。
前端主要功能如下:页面的实现、连接WebSocket并完成数据的接收以及回写、数据的发送。
部分源码如下:
1、 首先引入以下依赖
Xterm.js
npm install --save xterm
xterm-addon-fit
xterm.js的插件,使终端的尺寸适合包含元素。
npm install --save xterm-addon-fit
2、 创建一个command界面组件terminal.vue
主要包括xterm的初始化和websocket发送接收消息的实现
<template> <div id="xterm" class="xterm"/></template><script>import 'xterm/css/xterm.css'import {Terminal} from 'xterm'import {FitAddon} from 'xterm-addon-fit'export default { name: "terminal", props: { // 终端信息 hostInfo: {type: Object}, }, mounted() { this.initTerm(); }, beforeDestroy() { this.socket.close(); this.term.dispose(); }, data() { return { term: null, socket: null, } }, methods: { //初始化Xterm initTerm() { const term = new Terminal({ cursorBlink: true, 光标闪烁 cursorStyle: "block", 光标样式 null | 'block' | 'underline' | 'bar' scrollback: 800, 回滚 tabStopWidth: 8, 制表宽度 screenKeys: true, }); const fitAddon = new FitAddon(); term.loadAddon(fitAddon); term.open(document.getElementById('xterm')); fitAddon.fit(); term.focus(); this.term = term; this.initSocket(); }, //初始化websocket initSocket() { let endpoint = `${location.protocol === 'https' ? 'wss' : 'ws'}://${location.host}/ogg_server/webssh`; if (window.WebSocket) { this.socket = new WebSocket(endpoint);//如果支持websocket } else { this.term.write('Error: WebSocket Not Supported\r\n');//否则报错 return; } this.socketOnOpen(); this.socketOnMessage(); this.socketOnClose(); this.socketOnError(); }, socketOnOpen() { this.socket.onopen = () => { this.term.write('Connecting...\r\n'); this.socket.send(JSON.stringify({ operate: 'connect', host: this.hostInfo.host,//IP port: this.hostInfo.port,//端口号 username: this.hostInfo.username,//用户名 password: this.hostInfo.password//密码 })); this.term.onData((data) => { //发送指令 this.socket.send(JSON.stringify({"operate": "command", "command": data})); }); } }, socketOnMessage() { let _this = this; this.socket.onmessage = (evt) => { let data = evt.data.toString(); _this.term.write(data); } }, socketOnClose() { this.socket.onclose = () => { // console.log('close socket') } }, socketOnError() { let _this = this; this.socket.onerror = (error) => { //连接失败回调 _this.term.write('Error: ' + error + '\r\n'); } }, sendMessege(messege) { //发送指令 this.socket.send(JSON.stringify({"operate": "command", "command": messege})); } }}</script>
3、 使用时引用terminal.vue组件,传入主机信息即可
<template> <div> <terminal :host-info="hostInfo"></terminal> </div></template><script>import terminal from './terminal'export default { name: "test", components: {terminal}, data() { return { hostInfo: { host: '192.168.3.50',//IP port: '22',//端口号 username: 'root',//用户名 password: '123456'//密码 } } }}</script>
此时访问页面已经能看到终端屏幕,接下来继续实现后端逻辑。
后端功能主要包括服务器交互和请求转发两块,我们通过Jsch和Websocket来实现。服务器交互主要包括以下方法:初始化SSH连接、处理客户端发送的命令、读取命令执行结果、关闭连接等。前后端交互主要为对WebSocket生命周期中事件的处理,主要是连接WebSocket并完成数据的接收并回写。
部分源码如下:
1、Websocket配置,开启Websocket并配置处理器和拦截器。
2、Websocket处理器,实现Websocket生命周期事件,在接收到执行命令请求后调用执行逻辑。
3、WebSSH的业务逻辑,主要包括以下接口方法,重点看下处理客户端数据的实现,将请求数据分为连接请求和命令请求分别处理。
现在,我们已经成功实现了文章开始提出的Web端SSH需求。运行项目看下效果,可以看到已能实现类似SecureCRT的功能:
连接主机
ls命令
vim命令
top命令
评论
