zoukankan      html  css  js  c++  java
  • 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发送数据
    
  • 相关阅读:
    SpringMVC之数据处理及跳转
    SpringMVC之控制器Controller和RestFul风格
    第一个SpringMVC程序
    什么是SpringMVC
    Spring声明式事务
    【嘎】字符串-反转字符串
    【嘎】字符串-字符串中的单词数
    【嘎】二叉树-226. 翻转二叉树
    【嘎】数组-面试题 01.07. 旋转矩阵-看题解
    【嘎】字符串-统计位数为偶数的数字
  • 原文地址:https://www.cnblogs.com/xinzaiyuan/p/12389209.html
Copyright © 2011-2022 走看看