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

吾爱主题 阅读:137 2022-11-14 15:25:00 评论:0

本来是想通过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

可以去百度分享获取分享代码输入这里。
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

【腾讯云】云服务器产品特惠热卖中
搜索
标签列表
    关注我们

    了解等多精彩内容