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协议。)
yum install libssh2 libssh2-devel
然后通过pcel安装ssh2拓展 ,找准版本
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安装即可:
composer require phpseclib/phpseclib:~3.0
二、编写代码
测试Demo:http://cname.teiao.com:5707/
通过swoole创建一个WEBSocket,连接成功时创建一个协程专门读取ssh返回的内容发送到websocket,客户端发送消息时转发给shell。
以下是简单的功能实现,不可应用于生产,经测试,实际使用过程中某些命令的输出需要进行特殊处理。
1、swoole.php
<?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 () {
$server = new Server('0.0.0.0', 5707, false);
$server->handle('/ws', function (Request $request, Response $ws) {
$ws->upgrade();
$ssh = new SSH2('localhost',22);
if (!$ssh->login('root', 'Qq461625091@')) {
$ws->close();
return;
}
$ssh->setTimeout(0.1);
$subscribe=function () use($ws,$ssh){
$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
<?php
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());
});
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、运行
php swoole.php
以上就是PHP+Swoole实现web版的shell客户端详解的详细内容,更多关于PHP Swoole shell客户端的资料请关注其它相关文章!
相关文章