您好,登錄后才能下訂單哦!
這篇“如何用PHP實現(xiàn)一個IP防火墻”文章的知識點大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“如何用PHP實現(xiàn)一個IP防火墻”文章吧。
使用
運行簡單穩(wěn)定
方法接口簡單
內(nèi)置進(jìn)程守護(hù)
$worker = new Worker('tcp:0.0.0.0:' . Config::get('door.port_in')); // 監(jiān)聽一個端口 $worker->count = 2; // 設(shè)置多進(jìn)程 $worker->onConnect = function (TcpConnection $connection) { // 獲取IP白名單 $list_ip = AppIp::where('status', 0)->cache(3)->column('ip'); $remote_ip = $connection->getRemoteIp(); // 攔截IP if (!in_array($remote_ip, $list_ip)) { $connection->close(); } // 放行連接,連接內(nèi)部目標(biāo)端口 $to_connection = new AsyncTcpConnection('tcp:127.0.0.1:' . Config::get('door.port_to')); // 互相轉(zhuǎn)發(fā)流量 $connection->pipe($to_connection); $to_connection->pipe($connection); $to_connection->connect(); }
正如上面代碼所示,只有簡單幾行,便實現(xiàn)了
為了項目開發(fā)方便,我都會使用
最終實現(xiàn)的命令行效果如下:
運行命令 php think door start php think door start --mode d // 守護(hù)進(jìn)程重啟 重啟 php think door restart 停止 php think door stop
<?php declare(strict_types=1); namespace app\common\command; use think\console\Command; use think\console\Input; use think\console\input\Argument; use think\console\input\Option; use think\console\Output; class Door extends Command { protected function configure() { // 指令配置 $this->setName('door') // 設(shè)置think的命令參數(shù) ->addArgument('action', Argument::OPTIONAL, "start|stop|restart|reload|status|connections", 'start') ->addOption('mode', 'm', Option::VALUE_OPTIONAL, 'Run the workerman server in daemon mode.') ->setDescription('the door command'); } protected function execute(Input $input, Output $output) { // 指令輸出 $output->writeln('door'); $action = $input->getArgument('action'); $mode = $input->getOption('mode'); // 重新構(gòu)造命令行參數(shù),以便兼容workerman的命令 global $argv; $argv = []; array_unshift($argv, 'think', $action); if ($mode == 'd') { $argv[] = '-d'; } else if ($mode == 'g') { $argv[] = '-g'; } // ...workerman的代碼 } }
在上面的代碼中,主要做了兩件事:
實現(xiàn)
的命令設(shè)置
將命令參數(shù)重新構(gòu)造為
兼容的方式
使用
最終效果如下:
我們的
首先我們將第一個步中,流量轉(zhuǎn)發(fā)部分的代碼改造成如下的樣子:
<?php // 向TO發(fā)起連接 $to_connection = new AsyncTcpConnection('tcp://127.0.0.1:' . Config::get('door.port_to')); $to_connection->onMessage = function ($source, $data) use ($connection, $remote_ip) { // 接收到來自TO的數(shù)據(jù),返回的數(shù)據(jù) $connection->send($data); // 將流量統(tǒng)計存儲到內(nèi)存里 Cache::inc(md5($remote_ip) . '-to', strlen($data)); }; // 流程和流量控制 $to_connection->onClose = function ($source) use ($connection) { $connection->close(); }; $connection->onBufferFull = function ($dest) use ($to_connection) { $to_connection->pauseRecv(); }; $connection->onBufferDrain = function ($dest) use ($to_connection) { $to_connection->resumeRecv(); }; $connection->onMessage = function ($source, $data) use ($to_connection, $remote_ip) { // 接收來自IN的數(shù)據(jù),請求的數(shù)據(jù) $to_connection->send($data); // 將流量統(tǒng)計存儲到內(nèi)存里 Cache::inc(md5($remote_ip) . '-in', strlen($data)); }; // 流程和流量控制 $connection->onClose = function ($source) use ($to_connection) { $to_connection->close(); }; $to_connection->onBufferFull = function ($dest) use ($connection) { $connection->pauseRecv(); }; $to_connection->onBufferDrain = function ($dest) use ($connection) { $connection->resumeRecv(); };
在第一部的代碼中,只用兩行便實現(xiàn)了這些代碼:
// 放行連接,連接內(nèi)部目標(biāo)端口 $to_connection = new AsyncTcpConnection('tcp:127.0.0.1:' . Config::get('door.port_to')); // 互相轉(zhuǎn)發(fā)流量 $connection->pipe($to_connection); $to_connection->pipe($connection);
這里使用的是
這里我們將統(tǒng)計的數(shù)據(jù)存儲到緩存里,而不是直接連接數(shù)據(jù)庫更新,這是為了更好的連接性能。我們會另外開啟一個進(jìn)程將這些改動更新到數(shù)據(jù)庫。后面會介紹到。
我們將第一步中的加載
<?php $worker->onConnect = function (TcpConnection $connection) { $disable_cache_key = 'disable_ip_list'; $list_ip = Cache::get($disable_cache_key); if (empty($list_ip)) { $connection->close(); } $remote_ip = $connection->getRemoteIp(); if (!in_array($remote_ip, $list_ip)) { AppIpReject::initRecord($remote_ip); $connection->close(); } };
在這里我們不連接數(shù)據(jù)庫查詢,而是直接從本地緩存讀取白名單,這樣會有更好的性能。我們會在另一個進(jìn)程中更新這份白名單。
另外我們可以看到,攔截的
上面我們介紹,我們會另外開啟一個進(jìn)程,維護(hù)
<?php $worker_ip = new Worker(); $worker_ip->name = 'report'; $worker_ip->onWorkerStart = function () { Timer::add(5, function () { $disable_cache_key = 'disable_ip_list'; $list_ip = AppIp::where('status', 1)->column('ip'); Cache::set($disable_cache_key, $list_ip); foreach ($list_ip as $ip) { $ip_md5 = md5($ip); $in_length = Cache::pull("$ip_md5-in"); // 請求的數(shù)據(jù) $to_length = Cache::pull("$ip_md5-to"); // 返回的數(shù)據(jù) if (!empty($in_length) || !empty($to_length)) { $model_ip = AppIp::where('ip', $ip)->find(); $model_ip->in_buffer += $in_length; $model_ip->to_buffer += $to_length; $model_ip->save(); } } }); };
他做的事情很簡單,讀取緩存,更新數(shù)據(jù)到數(shù)據(jù)庫,并且更新
以上,只有幾行代碼,幾個小時(如果不含設(shè)計系統(tǒng)的時間,代碼量可能只有一兩個小時
這里使用的是
版權(quán)聲明:本文由phpreturn.com(
但是使用內(nèi)存緩存,
但實際上,
目前攔截
實際上,我們可以將客戶端的代碼,另外開一個項目,使客戶端和面板獨立開。在面板上實現(xiàn)通用得
但是這樣也帶來的更多的工作量,這種情況下,我們自然而然的認(rèn)為客戶端的環(huán)境不安全,所以要做權(quán)限認(rèn)證,登錄認(rèn)證。接口開發(fā)也要寫更多的代碼。
以上就是關(guān)于“如何用PHP實現(xiàn)一個IP防火墻”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。