Django結合Websocket進行WebSSH的實現


什么是webssh?

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

模型

此種 實現方式,將通過結合 以及后端的 來進行實現,所需要的技術 棧如下

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

 

技術介紹  

  xterm

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

  websocket

    這里通過websocket進行瀏覽器與django的數據交通橋梁

  paramiko

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

 

前端實現

vue發送websocket請求

<template>
    <div>
        <input type="text" v-model="message">
        <p><input type="button" @click="send" value="發送"></p>
        <p><input type="button" @click="close_socket" value="關閉"></p>
    </div>
</template>


<script>
export default {
    name:'ws',
    data() {
        return {
            message:'',
            testsocket:''
        }
    },
    methods:{
        send(){
           
        //    send  發送信息
        //    close 關閉連接

           this.testsocket.send(this.message)
           this.testsocket.onmessage = (res) => {
            console.log("WS的返回結果",res.data);
          
      }

        },
        close_socket(){
            this.testsocket.close()
        }

    },
    mounted(){
        this.testsocket = new WebSocket("ws://127.0.0.1:8000/ws/") 


        // onopen     定義打開時的函數
        // onclose    定義關閉時的函數
        // onmessage  定義接收數據時候的函數
        this.testsocket.onopen = function(){
            console.log("開始連接socket")
        },
        this.testsocket.onclose = function(){
            console.log("socket連接已經關閉")
        }
    }
}
</script>
ws.vue

安裝xterm

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

class ChatService(WebsocketConsumer):
    # 當Websocket創建連接時
    def connect(self):
        #websocket保持連接
        self.accept()  
        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

 

 

 

 

 

 

 


免責聲明!

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



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