Django 结合Websocket进行WebSSH的实现

模型

  • 此时webSSH实现方式,将通过结合websocket以及Paramiko来进行实现,所需要的技术栈如下
# 前端
vue
websocket
xterm.js  ["xterm": "^3.1.0"]

# 选择xterm插件的版本是3.1.0,在package.json里标记
"dependencies": {
    "axios": "^0.19.0",
    "vue": "^2.5.2",
    "vue-router": "^3.0.1",
    -----------------
    "xterm": "^3.1.0"
    -----------------
  },
#后端
django
dwebsocket
paramiko
threading

技术介绍

  • xterm
前端样式通过xterm插件进行shell黑窗口环境的搭建,这个插件会自动解析由后台paramiko返回的带有标记样式的命令结果,并渲染到浏览器中,非常炫酷。
  • websocket
这里通过websocket进行浏览器与django的数据交通桥梁
  • paramiko
paramiko此时的角色用来承担django与linux环境的交互,将前端发来的命令发给后台,将后台发来的命令结果返回到前端的xterm组件中

前端实现

前端xterm组件使用:简单

  • 安装xterm
cnpm install xterm --save

#使用命令默认安装的是最新版本,在这里我们在vue根目录下,package.json下的"dependencies":{"xterm": "^3.1.0"}指定xterm版本
  • vue框架中的引入xterm的样式文件
# 在vue main.js中引入xterm样式   *重*
import Vue from 'vue'
import App from './App'
import router from './router'
import 'xterm/dist/xterm.css'      //导入所需要的样式
 
Vue.config.productionTip = false;
import axios from 'axios'
Vue.prototype.axios = axios;
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

# 如果导入样式服务报错,解决方法如下

# 在vue项目的根目录下创建一个postcss.config.js文件夹

module.exports = { plugins: {
        'autoprefixer': {browsers: 'last 5 version'}
    }};
  • 初始化xterm组件并添加俩个插件:attach可以将终端附加到websocket流中,fit可以调整终端的大小以及和列适配父级元素
import {Terminal} from 'xterm'                                   //初始化id="terminal"
import * as attach from 'xterm/lib/addons/attach/attach'         //黑窗口绑定
import * as fit from 'xterm/lib/addons/fit/fit'                  //自动化调节

Terminal.applyAddon(attach);   //Addon插件
Terminal.applyAddon(fit);

  • 构建websocket并绑定到终端,websocket地址为ws协议前缀,此时使用的是即将在django中配置websocket后台视图的路由,这一系列行为挂载到钩子函数下进行
# 需要创建一个vue组件
# 例:console.vue *重*
<template>
    <div id="terminal">

    </div>

</template>

<script>
    import {Terminal} from 'xterm'                                   //初始化id="terminal"
    import * as attach from 'xterm/lib/addons/attach/attach'         //黑窗口绑定
    import * as fit from 'xterm/lib/addons/fit/fit'                  //自动化调节

    Terminal.applyAddon(attach);   //Addon插件
    Terminal.applyAddon(fit);
    export default {
        name: "console",
        mounted() {
            let terminalContainer = document.getElementById('terminal');
            this.term = new Terminal(this.terminal);    //初始化终端
            this.term.open(terminalContainer)           //把div为terminal的标签绑定到初始化号的黑窗口

            this.terminalSocket = new WebSocket('ws://127.0.0.1:8000/webssh/')

            this.term.attach(this.terminalSocket) //绑定黑窗口到websocket上

            this.terminalSocket   //连接对象
        },
        beforeDestroy() {
            this.terminalSocket.close()   //先关闭websocket
            this.term.destroy()   //关闭黑窗口
        }
    }
</script>

--------------------------------------------------------------------------------------------------------------------------------------------
# 提示语发送成功,失败,等信息语。
this.terminalSocket.onopen = function () {
    console.log('websocket is Connected...')
};
this.terminalSocket.onclose = function () {
    console.log('websocket is Closed...')
};
this.terminalSocket.onerror = function () {
    console.log('damn websocket is broken!')
}

  • 当浏览器关闭时,也代表着客户端关闭,此时主动断开连接,交给vue的钩子函数来处理这个问题
//钩子函数
beforeDestroy() {
    this.terminalSocket.close()   //先关闭websocket
    this.term.destroy()   //关闭黑窗口
}

后端实现

django 这里使用dwebsocket模块进行ws的服务端编写通信

  • 首先确定路由,也是前端的ws连接地址
# urls.py   路由

from django.contrib import admin
from django.urls import path,include
from deam import views
urlpatterns = [
    path('webssh/',views.Webssh)
]
  • 定义函数,初始化SSH连接对象(连接linux)
# views.py *重*
from dwebsocket import accept_websocket
from threading import Thread
import paramiko
# pip install paramiko
# pip install dwebsocket
def ssh_channle():
    host = '120.27.23.63'
    username = 'root'
    password = 'xxxxxxxxxxxx'
    sh = paramiko.SSHClient()    # 利用paramiko建立连接
    sh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    #保存校验密钥
    sh.connect(host,username=username,password=password)
    channle = sh.invoke_shell(term='xterm')
    return channle
  • 由于在SSH通道中,服务端可能返回结果的方式并不是与客户端发起的命令一唱一和,可能是一唱多和,比如类似top这样的命令,一次命令输入之后,服务端会返回N次结果,此时在django视图中采用多线程,专门处理命令结果的返回:以下是这个任务线程函数的定义:
# views.py *重*
FLAG = [True]
def cmd_callback(ws,channle):
    # 接收CMD命令的返回,并且给到websocket
    while True:
        if FLAG[0]:
            try:
                result = channle.recv(1024)  # 管道的返回值
                ws.send(result)      # 把结果返回给前端用户
            except:
                break
    return  #线程结束
# views.py *重*
@accept_websocket      #确保当前视图只接收websocket
def Webssh(request):
    if request.is_websocket:
        # 用户构建好了链接
        # 接受命令发给Linux
        ws = request.websocket
        channle = ssh_channle()   # 链接paramiko管道,与linux进行交互
        cmd_callback_thread = Thread(target=cmd_callback,args=(ws,channle))
        cmd_callback_thread.start()
        while True:
            try:
                cmd = ws.wait()     # 阻塞接受呃前端发来的命令
                print(cmd,'*******************************************************')
                if not cmd:
                    break
                channle.send(cmd)   # 向linux发送cmd
            except:
                break
        FLAG[0] = False
        ws.close()
        channle.close()
        return Response({'code':1})

重点

# websocket   ws.wait()接受vue发送的数据
#             ws.send()向vue发送数据

# paramiko    channle.recv(1024) 接受linux接受的数据
#             channle.send()   向linux发送数据
posted @ 2020-03-01 14:43  Mr-刘  阅读(821)  评论(0编辑  收藏