PHP+Swoole實(shí)現(xiàn)web版的shell客戶端詳解
本來(lái)是想通過PHP的proc_open和進(jìn)程進(jìn)行交互,可是中間的坑太多了,不得不轉(zhuǎn)換一下思路,然后想起來(lái)寶塔有網(wǎng)頁(yè)版shell客戶端,然后研究了一下,嘿嘿,發(fā)現(xiàn)能成 。
一、前期準(zhǔn)備
PHP連接ssh是基于第三方拓展庫(kù),PECL/ssh2( libssh2的php擴(kuò)展,允許php程序調(diào)用libssh2中的函數(shù))
然后有一個(gè)現(xiàn)成的、封裝好大部分常用操作的庫(kù)phpseclib
通過swoole的協(xié)程實(shí)現(xiàn)SSH的讀和寫并發(fā)進(jìn)行以及websocket和瀏覽器進(jìn)行通信。
1、安裝ssh2拓展庫(kù)
1.1、Linux安裝
首先要安裝libssh2(libssh2是一個(gè)C 函數(shù)庫(kù),用來(lái)實(shí)現(xiàn)SSH2協(xié)議。)
yum?install?libssh2?libssh2-devel?
然后通過pcel安裝ssh2拓展 ,找準(zhǔn)版本
pecl install ssh2-1.1.2
當(dāng)然也可以通過phpize進(jìn)行手動(dòng)安裝。
1.2、window安裝
libssh2好像一般都有,沒有就下載丟到系統(tǒng)里,主要是安裝ssh2。根據(jù)自己PHP的版本去下載,可以看下自己的php版本,以及是32位的還是64位的,32位的下載x86, 64位的下載x64
php.ini中加入 extension=php_ssh2.dll ,完事。
2、swoole安裝
參考官網(wǎng):https://wiki.swoole.com/#/environment
3、phpseclib
官網(wǎng):https://phpseclib.com,composer安裝即可:
composer?require?phpseclib/phpseclib:~3.0
二、編寫代碼
測(cè)試Demo:http://cname.teiao.com:5707/
通過swoole創(chuàng)建一個(gè)websocket,連接成功時(shí)創(chuàng)建一個(gè)協(xié)程專門讀取ssh返回的內(nèi)容發(fā)送到websocket,客戶端發(fā)送消息時(shí)轉(zhuǎn)發(fā)給shell。
以下是簡(jiǎn)單的功能實(shí)現(xiàn),不可應(yīng)用于生產(chǎn),經(jīng)測(cè)試,實(shí)際使用過程中某些命令的輸出需要進(jìn)行特殊處理。
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; /* * 設(shè)置協(xié)程運(yùn)行相關(guān)的參數(shù) * */ Co::set([ 'socket_timeout'=>-1, //tcp超時(shí) 'hook_flags' => SWOOLE_HOOK_ALL //HOOK函數(shù)范圍 ]); /* * 創(chuàng)建協(xié)程容器 * */ run(function () { /* * 第三個(gè)參數(shù) 代表是否開啟ssl * */ $server = new Server('0.0.0.0', 5707, false); $server->handle('/ws', function (Request $request, Response $ws) { /*websocket協(xié)議*/ $ws->upgrade(); /*連接ssh*/ $ssh = new SSH2('localhost',22); /*如果登錄失敗*/ if (!$ssh->login('root', 'Qq461625091@')) { $ws->close(); return; } /*命令輸出內(nèi)容的讀取時(shí)間*/ $ssh->setTimeout(0.1); /* * 創(chuàng)建協(xié)程,專門輸出命令行內(nèi)容 * */ $subscribe=function () use($ws,$ssh){ /* * 保存id,用于取消協(xié)程 * */ $ws->Gid = go(function () use ($ws,$ssh){ /* * 協(xié)程退出時(shí)清理 * */ 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);//記錄退出原因 /* * 如果協(xié)程已經(jīng)運(yùn)行 * */ if(isset($ws->Gid)){ Coroutine::cancel($ws->Gid); //關(guān)閉協(xié)程 } $ws->close(); //斷開ws }; /* * 正常處理邏輯 * */ $subscribe(); //開始訂閱 $cmd=[ 'ps -ef', 'ping 127.0.0.1', 'ifconfig', "\x03" ]; while (true) { $frame = $ws->recv(); //阻塞接收消息 if ($frame === '') { $quit("斷開連接,收到空數(shù)據(jù)!"); break; } else if ($frame === false) { $quit(swoole_last_error()); break; } else { if ($frame->data == 'close' || get_class($frame) === CloseFrame::class) { $quit("用戶主動(dòng)關(guān)閉\n"); break; } /* * 如果不在測(cè)試命令,則終止 * */ if(!in_array($frame->data,$cmd)){ continue; } $ssh->write($frame->data."\n"); // note the "\n" } } }); /* * 輸出默認(rèn)測(cè)試模板 * */ $server->handle('/', function (Request $request, Response $response) { $response->end(getTest()); }); $server->start(); });
2、function.php
<?php /* * 打印測(cè)試的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 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測(cè)試</h1> <div>須知:測(cè)試環(huán)境只支持:ps -ef、ping 127.0.0.1、ifconfig,三個(gè)命令。</div> <div>提示:回車提交、ctrl+c中斷(終端現(xiàn)在連接的是網(wǎng)站的主機(jī))</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(); /* * 自動(dòng)聚焦 * */ $(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、運(yùn)行
php swoole.php
以上就是PHP+Swoole實(shí)現(xiàn)web版的shell客戶端詳解的詳細(xì)內(nèi)容,更多關(guān)于PHP Swoole shell客戶端的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
php將字符串轉(zhuǎn)換為數(shù)組實(shí)例講解
在本篇文章里小編給大家分享的是關(guān)于php將字符串轉(zhuǎn)換為數(shù)組實(shí)例講解,需要的朋友們可以學(xué)習(xí)下。2020-05-05PHP 利用Mail_MimeDecode類提取郵件信息示例
重點(diǎn)為one_mail函數(shù)。利用Mail_mimeDecode類從郵件中提取郵件頭和郵件正文,具體實(shí)現(xiàn)如下2014-01-01PHP中設(shè)置時(shí)區(qū),記錄日志文件的實(shí)現(xiàn)代碼
php中設(shè)置時(shí)區(qū) 記錄日志文件的實(shí)現(xiàn)代碼,需要的朋友可以參考下2013-01-01php中OR與|| AND與&&的區(qū)別總結(jié)
以下是對(duì)php中OR與|| AND與&&的區(qū)別進(jìn)行了詳細(xì)的總結(jié)介紹,需要的朋友可以過來(lái)參考下2013-10-10PHP實(shí)現(xiàn)圖片的等比縮放和Logo水印功能示例
這篇文章主要介紹了PHP實(shí)現(xiàn)圖片的等比縮放和Logo水印功能,結(jié)合實(shí)例形式分析了php圖片等比例縮放及l(fā)ogo水印添加操作技巧,需要的朋友可以參考下2017-05-05php數(shù)組函數(shù)序列之end() - 移動(dòng)數(shù)組內(nèi)部指針到最后一個(gè)元素,并返回該元素的值
end() 函數(shù)將數(shù)組內(nèi)部指針指向最后一個(gè)元素,并返回該元素的值(如果成功)。2011-10-10PHP 數(shù)組和字符串互相轉(zhuǎn)換實(shí)現(xiàn)方法
PHP 中由于數(shù)組和字符串這兩種變量類型是如此常用,以至于 PHP 具有兩個(gè)函數(shù),可以在字符串和數(shù)組之間互相進(jìn)行轉(zhuǎn)換2013-03-03php7.3報(bào)preg_match()?JIT?compilation?failed?no?more?mem
PHP?JIT編譯失敗,內(nèi)存不足的解決方法!你是否遇到過這個(gè)問題?不用擔(dān)心,我們將為你提供簡(jiǎn)單易懂的解決方案,讓你擺脫這一困擾,立即閱讀我們的指南,輕松解決PHP?JIT編譯失敗的煩惱!2023-12-12php 連接mssql數(shù)據(jù)庫(kù) 初學(xué)php筆記
如果實(shí)現(xiàn)了PHP和MySQL鏈接了,PHP和MSSQL的鏈接其實(shí)很簡(jiǎn)單; 支持MSSQL的本地鏈接和遠(yuǎn)程鏈接2010-03-03