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發送數據


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM