Django結合Websocket進行WebSSH的實現

什麼是webssh?

  泛指一種技術能夠在網頁上實現一個 終端。從而無需 之類的模擬終端工具進行 鏈接,將 這一比較低層的操做也從 架構扭成了 架構 這樣的架構經常使用在運維製做開發一些堡壘機等系統中,或是目前比較新型的在線教育方式,經過向學生提供一個能夠直接使用瀏覽器進行相關 操做或代碼編寫的學習方式 主要是創建客戶端與服務端的即時通訊css

模型

此種 實現方式,將經過結合 以及後端的 來進行實現,所須要的技術 棧以下前端

# 前端
vue websocket xterm.js
# 後端 
django dwebsocket (channels) paramiko threading

 

技術介紹  

  xtermvue

    前端經過xterm插件進行shell黑窗口環境的搭建,這個插件會自動解析由後臺paramiko返回的帶有標記樣式的命令結果,並渲染到瀏覽器中,很是酷炫linux

  websocketwebpack

    這裏經過websocket進行瀏覽器與django的數據交通橋樑web

  paramiko shell

    paramiko此時的角色用來承擔django與linux環境的交互,將前端發來的命令發送給後臺,將 後臺發來的命令結果返回到前端的xterm組件中npm

 

前端實現

安裝xtermdjango

cnpm install xterm@3.1.0 --save  //指定版本安裝

在vue框架中引入xterm的樣式文件編程

// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue'
import App from './App' import router from './router' import 'xterm/dist/xterm.css' // 看這裏,添加xterm css文件樣式
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({ el: '#app', router, components: { App }, template: '<App/>' })
main.js

使用xterm和websocket來實時發送命令

<template>
  <div class="console" id="terminal"></div>
</template>
<script> import { Terminal } from 'xterm' import * as attach from 'xterm/lib/addons/attach/attach' import * as fit from 'xterm/lib/addons/fit/fit' export default { name: 'webssh', data () { return { term: null, terminalSocket: null, order:'' } }, methods: { }, mounted () { //實例化一個websocket,用於和django江湖
      this.terminalSocket = new WebSocket("ws://127.0.0.1:8000/web/"); //獲取到後端傳回的信息
      this.terminalSocket.onmessage = (res) => { console.log(res.data); // var message = JSON.parse(res.data);
          //將傳回來的數據顯示在xterm裏
          this.term.writeln("\r\n"+res.data); //重置要發送的信息
          this.order = ""
          //換行,顯示下一個開頭
          this.term.write("\r\n$ "); } //ws鏈接的時候
    // this.terminalSocket.onopen = function(){
    // console.log('websocket is Connected...')
    // }
    //ws關閉的時候
    // this.terminalSocket.onclose = function(){
    // console.log('websocket is Closed...')
    // }
    //ws錯誤的時候
    // this.terminalSocket.onerror = function(){
    // console.log('damn Websocket is broken!')
    // }
    // this.term.attach(this.terminalSocket)
    // 綁定xterm到ws流中 },
 let terminalContainer = document.getElementById('terminal') //建立xterm實例
      this.term = new Terminal({ cursorBlink: true, // 顯示光標
        cursorStyle: "underline" // 光標樣式
        })                     // 建立一個新的Terminal對象
      
      this.term.open(terminalContainer)              // 將term掛載到dom節點上

      
      //在xterm上顯示命令行提示
      this.term.write('$ ') //監聽xterm的鍵盤事件
      this.term.on('key', (key, ev)=>{ // key是輸入的字符 ev是鍵盤按鍵事件
        console.log("key==========", ev.keyCode); this.term.write(key) // 將輸入的字符打印到黑板中
        if (ev.keyCode == 13) { // 輸入回車
            // console.log("輸入回車")
            // this.term.write('$ ')
            // console.log(this.order)
            
            //使用webscoket將數據發送到django
            this.terminalSocket.send(this.order) // this.order=''
            console.log("裏面的order",this.order) }else if(ev.keyCode == 8){//刪除按鈕
          //截取字符串[0,lenth-1]
          this.order = this.order.substr(0,this.order.length-1) //清空當前一條的命令
          this.term.write("\x1b[2K\r") //簡化當前的新的命令顯示上
          this.term.write("$ "+this.order) console.log("截取的字符串"+this.order) typeof this.order }else{// 將每次輸入的字符拼湊起來
        this.order += key console.log("外面的order",this.order)} }) }, } </script>
webssh.vue

 

後端實現

基於channels實現websocket

安裝channels

pip install channels

在setting的同級目錄下建立routing.py

#routing.py
from channels.routing import ProtocolTypeRouter application = ProtocolTypeRouter({ # 暫時爲空
})

配置setting

INSTALLED_APPS = [ 'channels' ] ASGI_APPLICATION = "項目名.routing.application"

 啓動帶有ASGI的django項目

  帶有ASGI的項目

 

   日常項目

在app-chats中建立一個wsserver.py文件夾來保存關於websocket的處理視圖

from channels.generic.websocket import WebsocketConsumer # 這裏除了 WebsocketConsumer 以外還有 # JsonWebsocketConsumer # AsyncWebsocketConsumer # AsyncJsonWebsocketConsumer # WebsocketConsumer 與 JsonWebsocketConsumer 就是多了一個能夠自動處理JSON的方法 # AsyncWebsocketConsumer 與 AsyncJsonWebsocketConsumer 也是多了一個JSON的方法 # AsyncWebsocketConsumer 與 WebsocketConsumer 纔是重點 # 看名稱彷佛理解並不難 Async 無非就是異步帶有 async / await # 是的理解並無錯,但對與咱們來講他們惟一不同的地方,可能就是名字的長短了,用法是如出一轍的 # 最誇張的是,基類是同一個,並且這個基類的方法也是Async異步的 # 因爲咱們對Async編程還未熟練,所在這邊使用相對簡單的WebsocketConsumer進行演示

class ChatService(WebsocketConsumer): # 當Websocket建立鏈接時
    def connect(self): pass
    
    # 當Websocket接收到消息時
    def receive(self, text_data=None, bytes_data=None): pass
    
    # 當Websocket發生斷開鏈接時
    def disconnect(self, code): pass
wsserver.py

配置對應的路由

from django.urls import path from chats.chatService import ChatService websocket_url = [ path("ws/",ChatService) ]
url.py

在routing.py裏增長關於websocket的非http請求的url

from channels.routing import ProtocolTypeRouter,URLRouter from chats.urls import websocket_url application = ProtocolTypeRouter({ "websocket":URLRouter( websocket_url ) })
routing.py

 

Paramiko的使用

安裝paramiko

pip install paramiko

使用paramiko

from django.test import TestCase # Create your tests here.
import paramiko class WebSsh(object): def client_ssh(self): sh = paramiko.SSHClient()  # 1 建立SSH對象
        sh.set_missing_host_key_policy(paramiko.AutoAddPolicy())  # 2 容許鏈接不在know_hosts文件中的主機
        sh.connect("10.211.55.17", username="parallels", password="beijing")  # 3 鏈接服務器
        stdin, stdout, stderr = sh.exec_command('ls') right_info = stdout.read() err_info = stderr.read() if right_info: print(right_info.decode("utf-8")) elif err_info: print(err_info.decode("utf-8")) else: print("命令執行成功") if __name__ == '__main__': a = WebSsh() a.client_ssh()

 

 webssh的後端實現

INSTALLED_APPS=[ 'channels', 'chats', ] ASGI_APPLICATION = "shiyanloupro.routing.application"
shiyanloupro/setting.py
from channels.routing import ProtocolTypeRouter,URLRouter from chats.urls import websocket_url application = ProtocolTypeRouter({ "websocket":URLRouter( websocket_url ) })
shiyanloupro/routing.py
from django.urls import path from chats.chatservice import ChatService,WebSSHService websocket_url = [ path("ws/",ChatService), path("web/",WebSSHService), ]
chats/urls.py
from channels.generic.websocket import WebsocketConsumer import paramiko socket_list = [] class ChatService(WebsocketConsumer): # 當Websocket建立鏈接時
    def connect(self): self.accept() socket_list.append(self) # 當Websocket接收到消息時
    def receive(self, text_data=None, bytes_data=None): print(text_data)  # 打印收到的數據
        for ws in socket_list:  # 遍歷全部的WebsocketConsumer對象
            ws.send(text_data)  # 對每個WebsocketConsumer對象發送數據


    # 當Websocket發生斷開鏈接時
    def disconnect(self, code): print(f'sorry{self},你被女友拋棄了') socket_list.remove(self) class WebSSHService(WebsocketConsumer): def connect(self): self.accept() self.sh = paramiko.SSHClient()  # 1 建立SSH對象
        self.sh.set_missing_host_key_policy(paramiko.AutoAddPolicy())  # 2 容許鏈接不在know_hosts文件中的主機
        self.sh.connect("10.211.55.17", username="parallels", password="beijing")  # 3 鏈接服務器
        print("鏈接成功") def receive(self, text_data=None, bytes_data=None): print(str(text_data))  # 打印收到的數據
        print(type(text_data)) stdin, stdout, stderr = self.sh.exec_command(text_data) right_info = stdout.read() err_info = stderr.read() print(right_info) if right_info: new_data = right_info.decode("utf-8").replace("\n","\r\n") print(new_data) self.send(new_data) elif err_info: new_data = err_info.decode("utf-8").replace("\n", "\r\n") print(new_data) self.send(new_data) else: print(self.send("命令執行成功")) def disconnect(self, code): print(f'sorry{self},你被女友拋棄了')
chats/chatservice.py
相關文章
相關標籤/搜索