Vue+websocket+django实现WebSSH demo

目标:

        通过实例说明,能够应用至自身应用中




效果:

        如下图,实现了webssh,可以通过web方式,进行实时命令处理

Vue+websocket+django实现WebSSH demo_第1张图片




前端:

        包依赖:"xterm": "^4.14.1",   "xterm-addon-fit": "^0.5.0"

        封装SSH组件:/components/SSH/index.vue






        父组件引用:







后台:

        Django==3.1.10  daphne==3.0.2  paramiko==2.8.0  channels==3.0.4

         假定目录结构如下:

Vue+websocket+django实现WebSSH demo_第2张图片

        (1) 修改asgi.py 

        Tutorial Part 1: Basic Setup — Channels 3.0.4 documentation  

import os

from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')

application = ProtocolTypeRouter({  # 区分http or ws请求
    "http": get_asgi_application(),
})

         (2) 修改settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'channels'  # 添加
]


ASGI_APPLICATION = 'project.asgi.application'
CHANNEL_LAYERS = {
    'default': {
        "BACKEND": "channels.layers.InMemoryChannelLayer"  # 默认用内存
    },
}

 信道中间层用redis 可参加 https://channels.readthedocs.io/en/stable/topics/channel_layers.html

        (3)配置路由

        settings.py

import os

from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter

from consumer import routing  # 添加路由

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    'websocket': routing.ws_router  # ws请求入口
})

        routing.py

from channels.auth import AuthMiddlewareStack
from channels.routing import URLRouter
from django.urls import path

from consumer.consumers import SSHConsumer

ws_router = AuthMiddlewareStack(
    URLRouter([
        path(r'ws/ssh/', SSHConsumer.as_asgi()),
    ])
)

        consumers.py

import json
from threading import Thread

from channels.generic.websocket import WebsocketConsumer
import paramiko


class SSHConsumer(WebsocketConsumer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.ip = None
        self.chan = None
        self.ssh = None

    def connect(self):
        self.ip = self.scope['url_route']['kwargs']['ip']

        self.accept()
        self._init()

    def disconnect(self, close_code):
        self.chan.close()
        self.ssh.close()

    def get_client(self):
        p_key = paramiko.RSAKey.from_private_key_file("/root/.ssh/id_rsa")  # ssh免密登录私钥
        ssh = paramiko.SSHClient()
        ssh.load_system_host_keys()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(hostname=self.ip, port=22, username='root', pkey=p_key)

        return ssh

    def loop_read(self):
        while True:
            data = self.chan.recv(32 * 1024)
            if not data:
                self.close(1234)
                break

            self.send(bytes_data=data)

    def _init(self):
        self.send(bytes_data=b'Connecting ...\r\n')

        try:
            self.ssh = self.get_client()
        except Exception as e:
            self.send(bytes_data=f'Exception: {e}\r\n'.encode())
            self.close()
            return

        self.chan = self.ssh.invoke_shell(term='xterm')
        self.chan.transport.set_keepalive(30)

        Thread(target=self.loop_read).start()

    def receive(self, text_data=None, bytes_data=None):
        data = text_data or bytes_data
        if data:
            data = json.loads(data)

            resize = data.get('resize')
            if resize and len(resize) == 2:
                self.chan.resize_pty(*resize)
            else:
                self.chan.send(data['data'])

启动命令:

uwsgi --ini project_uwsgi.ini   # 对应8000
daphne project.asgi:application -p 8001 -b 0.0.0.0  --access-log log/asgi.log

Nginx配置:

upstream app {
    server ip:8000;
}

upstream socket {
    server ip:8001;
}

server{
        listen 80;
        server_name _;

        location / {
                root   /usr/share/nginx/html;
                try_files $uri $uri/ /index.html;
        }

        location ~/app/ { # http入口
                rewrite ^/app/(.*) /$1 break;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Host $server_name;
        }
        location ~/socket/ {  # websocket入口
                rewrite ^/socket/(.*) /$1 break;
                proxy_pass http://socket;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_redirect off;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Host $server_name;
        }
}

你可能感兴趣的:(Devops平台,前端,vue.js,websocket,django)