服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - PHP教程 - PHP+Swoole实现web版的shell客户端详解

PHP+Swoole实现web版的shell客户端详解

2022-11-14 15:25PHP开源社区 PHP教程

这篇文章主要为大家详细介绍了如何利用PHP+Swoole实现web版的shell客户端,文中的示例代码讲解详细,感兴趣的小伙伴可以尝试一下

本来是想通过PHP的proc_open和进程进行交互,可是中间的坑太多了,不得不转换一下思路,然后想起来宝塔有网页版shell客户端,然后研究了一下,嘿嘿,发现能成 。

一、前期准备

PHP连接ssh是基于第三方拓展库,PECL/ssh2( libssh2的php扩展,允许php程序调用libssh2中的函数)

然后有一个现成的、封装好大部分常用操作的库phpseclib

通过swoole的协程实现SSH的读和写并发进行以及websocket和浏览器进行通信。

1、安装ssh2拓展库

1.1、Linux安装

首先要安装libssh2(libssh2是一个C 函数库,用来实现SSH2协议。)

?
1
yum install libssh2 libssh2-devel 

然后通过pcel安装ssh2拓展 ,找准版本

?
1
pecl install ssh2-1.1.2

当然也可以通过phpize进行手动安装。

1.2、window安装

libssh2好像一般都有,没有就下载丢到系统里,主要是安装ssh2。根据自己PHP的版本去下载,可以看下自己的php版本,以及是32位的还是64位的,32位的下载x86, 64位的下载x64

下载地址

php.ini中加入 extension=php_ssh2.dll ,完事。

2、swoole安装

参考官网:https://wiki.swoole.com/#/environment

3、phpseclib

官网:https://phpseclib.com,composer安装即可:

?
1
composer require phpseclib/phpseclib:~3.0

二、编写代码

测试Demo:http://cname.teiao.com:5707/

通过swoole创建一个websocket,连接成功时创建一个协程专门读取ssh返回的内容发送到websocket,客户端发送消息时转发给shell。

以下是简单的功能实现,不可应用于生产,经测试,实际使用过程中某些命令的输出需要进行特殊处理。

1、swoole.php

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
<?php
 
include_once 'include/functions.php';
include_once 'vendor/autoload.php';
 
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\WebSocket\CloseFrame;
use Swoole\Coroutine\Http\Server;
 
use Swoole\Coroutine;
use function Swoole\Coroutine\go;
use function Swoole\Coroutine\run;
use function Swoole\Coroutine\defer;
use phpseclib3\Net\SSH2;
 
 
 
/*
 * 设置协程运行相关的参数
 * */
Co::set([
    'socket_timeout'=>-1, //tcp超时
    'hook_flags' => SWOOLE_HOOK_ALL  //HOOK函数范围
]);
 
 
/*
 * 创建协程容器
 * */
run(function () {
 
    /*
     * 第三个参数 代表是否开启ssl
     * */
    $server = new Server('0.0.0.0', 5707, false);
 
    $server->handle('/ws', function (Request $request, Response $ws) {
 
        /*websocket协议*/
        $ws->upgrade();
 
        /*连接ssh*/
        $ssh = new SSH2('localhost',22);
 
        /*如果登录失败*/
        if (!$ssh->login('root', 'Qq461625091@')) {
            $ws->close();
            return;
        }
 
        /*命令输出内容的读取时间*/
        $ssh->setTimeout(0.1);
 
 
 
        /*
         * 创建协程,专门输出命令行内容
         * */
        $subscribe=function () use($ws,$ssh){
 
 
            /*
             * 保存id,用于取消协程
             * */
            $ws->Gid = go(function () use ($ws,$ssh){
 
                /*
                 * 协程退出时清理
                 * */
                defer(function () use ($ssh,$ws) {
                    /*
                     * 退出
                     * */
                    logs($ws->qq.',已断开链接!');
                    $ssh->disconnect();
                });
 
 
                try {
 
                    while (true){
                        $msg=$ssh->read('username@username:~$');
                        if(!empty($msg)){
                            $ws->push($msg);
                        }
                    }
 
                } catch (\Throwable $e) {
                    logs('读取异常');
                }
 
            });
        };
 
 
        /*
         * 清理
         * */
        $quit=function ($log) use ($ws){
 
            logs($log);//记录退出原因
 
            /*
             * 如果协程已经运行
             * */
            if(isset($ws->Gid)){
                Coroutine::cancel($ws->Gid); //关闭协程
            }
 
            $ws->close(); //断开ws
 
        };
 
 
        /*
         * 正常处理逻辑
         * */
 
        $subscribe(); //开始订阅
 
        $cmd=[
            'ps -ef',
            'ping 127.0.0.1',
            'ifconfig',
            "\x03"
        ];
 
 
        while (true) {
 
            $frame = $ws->recv(); //阻塞接收消息
 
            if ($frame === '') {
 
                $quit("断开连接,收到空数据!");
                break;
 
            } else if ($frame === false) {
 
                $quit(swoole_last_error());
                break;
 
            } else {
 
                if ($frame->data == 'close' || get_class($frame) === CloseFrame::class) {
                    $quit("用户主动关闭\n");
                    break;
                }
 
                /*
                  * 如果不在测试命令,则终止
                  * */
                if(!in_array($frame->data,$cmd)){
                    continue;
                }
 
                $ssh->write($frame->data."\n"); // note the "\n"
 
            }
        }
    });
 
 
    /*
     * 输出默认测试模板
     * */
    $server->handle('/', function (Request $request, Response $response) {
        $response->end(getTest());
    });
 
    $server->start();
});

2、function.php

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<?php
 
/*
 * 打印测试的html模板
 * */
function getTest(): string
{
    $test = <<<HTML
        <!DOCTYPE html>
        <html lang="zh-cn" xmlns="http://www.w3.org/1999/html">
        <head>
            <meta charset="UTF-8"/>
            <meta charset="UTF-8"/>
            <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
            <title>Web SSH客户端</title>
            <link href="https://nicen.cn/wp-content/themes/document/favicon.ico" rel="external nofollow"  rel="shortcut icon" type="image/x-icon"/>
            <script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/jquery/3.6.0/jquery.min.js" type="application/javascript"></script>
            <script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/keyboardjs/2.6.2/keyboard.min.js" type="application/javascript"></script>
            <style>
                body{
                    background-color: #000000;
                    color: #e2e2e2;
                    padding: 15px;
                }
                 input{
                    background-color: black;
                    border: none;
                    color: white;
                    outline: none;
                    font-size: 17px;
                }
            </style>
        </head>
        <body>
        <h1>Web SSH测试</h1>
        <div>须知:测试环境只支持:ps -ef、ping 127.0.0.1、ifconfig,三个命令。</div>
        <div>提示:回车提交、ctrl+c中断(终端现在连接的是网站的主机)</div>
        <br />
        <main>
             <span id="content"></span>
             <input type="text">
        </main>
 
        </body>
        <script>
 
         window.onload=function (){
 
            let content=$("#content");
            let input= $('input');
            let wsServer = 'ws://cname.teiao.com:5707/ws';
            let websocket = new WebSocket(wsServer);
 
            websocket.onopen = function (evt) {
                content.append("Connected to WebSocket server.<br />");
            };
 
            websocket.onclose = function (evt) {
                content.append("Disconnected.<br />");
            };
 
            websocket.onmessage = function (evt) {
                content.append(evt.data.replaceAll("\\n",'<br />'));
                input.val("");
                $(window).scrollTop(document.documentElement.scrollHeight) 
            };
 
            websocket.onerror = function (evt, e) {
                content.append("Error occured: " + evt.data+"<br />");
            };
 
 
            input.focus();
 
            /*
            * 自动聚焦
            * */
            $(window).on("click",function (){
                input.focus();
            })
 
            /*
            * 回车提交
            * */
            keyboardJS.bind('enter', (e) => {
              websocket.send(input.val());
            });
 
            /*
            * ctrl+c
            * */
             keyboardJS.bind('ctrl > c', (e) => {
              websocket.send("\x03");
            });
        }
 
 
        </script>
HTML;
 
    return $test;
}
 
 
/*
 * 记录日志
 * */
function logs(string $log, bool $flag = true): void
{
    $time = date("Y-m-d H:i:s", time());
 
    if ($flag) {
        echo $time . ',' . $log . "\n";
    } else {
        file_put_contents('log.txt', $time . ',' . $log . "\n", FILE_APPEND);
    }
}

3、运行

?
1
php swoole.php

以上就是PHP+Swoole实现web版的shell客户端详解的详细内容,更多关于PHP Swoole shell客户端的资料请关注服务器之家其它相关文章!

原文链接:https://mp.weixin.qq.com/s/tvhEOWr4Wi8ejNsJiSYG8Q

延伸 · 阅读

精彩推荐