如何用PHP实现一个IP防火墙(IP,php,编程语言)

时间:2024-04-27 18:27:20 作者 : 石家庄SEO 分类 : 编程语言
  • TAG :

这篇“如何用PHP实现一个IP防火墙”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“如何用PHP实现一个IP防火墙”文章吧。

第一步,首先能够简简单单的过滤IP

使用PHP监听端口并且转发数据的框架很多,对此我选择workerman,原因有3

  • 运行简单稳定

  • 方法接口简单

  • 内置进程守护

如何用PHP实现一个IP防火墙

workerman的使用方法非常简单,只要10行代码,就实现了IP转发+白名单过滤:

$worker=newWorker('tcp:0.0.0.0:'.Config::get('door.port_in'));//监听一个端口$worker->count=2;//设置多进程$worker->onConnect=function(TcpConnection$connection){//获取IP白名单$list_ip=AppIp::where('status',0)->cache(3)->column('ip');$remote_ip=$connection->getRemoteIp();//拦截IPif(!in_array($remote_ip,$list_ip)){$connection->close();}//放行连接,连接内部目标端口$to_connection=newAsyncTcpConnection('tcp:127.0.0.1:'.Config::get('door.port_to'));//互相转发流量$connection->pipe($to_connection);$to_connection->pipe($connection);$to_connection->connect();}

正如上面代码所示,只有简单几行,便实现了IP监听和转发,其中IP白名单通过数据库查询,并且缓存。

第二步,与ThinkPHP命令行整合在一起

为了项目开发方便,我都会使用ThinkPHP框架进行开发,它够简单,功能也比较齐全。

如何用PHP实现一个IP防火墙

最终实现的命令行效果如下:

运行命令phpthinkdoorstartphpthinkdoorstart--moded//守护进程重启重启phpthinkdoorrestart停止phpthinkdoorstop

workerman的命令参数与thinkphp并不兼容,但是实现这样的效果并不难,实际上很简单,代码如下:

<?phpdeclare(strict_types=1);namespaceapp\common\command;usethink\console\Command;usethink\console\Input;usethink\console\input\Argument;usethink\console\input\Option;usethink\console\Output;classDoorextendsCommand{protectedfunctionconfigure(){//指令配置$this->setName('door')//设置think的命令参数->addArgument('action',Argument::OPTIONAL,"start|stop|restart|reload|status|connections",'start')->addOption('mode','m',Option::VALUE_OPTIONAL,'Runtheworkermanserverindaemonmode.')->setDescription('thedoorcommand');}protectedfunctionexecute(Input$input,Output$output){//指令输出$output->writeln('door');$action=$input->getArgument('action');$mode=$input->getOption('mode');//重新构造命令行参数,以便兼容workerman的命令global$argv;$argv=[];array_unshift($argv,'think',$action);if($mode=='d'){$argv[]='-d';}elseif($mode=='g'){$argv[]='-g';}//...workerman的代码}}

在上面的代码中,主要做了两件事:

  • 实现

    ThinkPHP

    的命令设置

  • 将命令参数重新构造为

    workerman

    兼容的方式

第三步,实现管理面板

使用PHP实现一个管理面板太简单了,PHP到处都是这样的后台框架,这里我选择ulthon_admin,这是我自己开发维护的,它基于ThinkPHP6,很简单,为定制而生,不搞所谓的“插件”和“市场”生态,能够自动生成CURD代码,并且内置几了几个有趣的皮肤。

最终效果如下:

如何用PHP实现一个IP防火墙如何用PHP实现一个IP防火墙

第四步,进阶,更好的性能和流量统计

我们的IP拦截客户端需要运行在服务器上,并且直接连接数据库,如果每次收到请求都要查询数据库,那么很有可能导致连接不通畅,尤其是客户端和数据库本身位置较远的时候。在第一步的代码中,我们只是简单的使用了查询缓存,但是还不够,还可以优化。并且我们可以在管理面板的截图中看到,我们是可以统计流量和拦截次数的,现在我们要实现这些功能:

流量统计

首先我们将第一个步中,流量转发部分的代码改造成如下的样子:

<?php//向TO发起连接$to_connection=newAsyncTcpConnection('tcp://127.0.0.1:'.Config::get('door.port_to'));$to_connection->onMessage=function($source,$data)use($connection,$remote_ip){//接收到来自TO的数据,返回的数据$connection->send($data);//将流量统计存储到内存里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的数据,请求的数据$to_connection->send($data);//将流量统计存储到内存里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();};

在第一部的代码中,只用两行便实现了这些代码:

//放行连接,连接内部目标端口$to_connection=newAsyncTcpConnection('tcp:127.0.0.1:'.Config::get('door.port_to'));//互相转发流量$connection->pipe($to_connection);$to_connection->pipe($connection);

这里使用的是workerman内置的流量转发,它很好用,但是这里我们要统计流量,所以我们手动转发流量。

这里我们将统计的数据存储到缓存里,而不是直接连接数据库更新,这是为了更好的连接性能。我们会另外开启一个进程将这些改动更新到数据库。后面会介绍到。

拦截统计

我们将第一步中的加载IP白名单的逻辑改成下面这样:

<?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();}};

在这里我们不连接数据库查询,而是直接从本地缓存读取白名单,这样会有更好的性能。我们会在另一个进程中更新这份白名单。

另外我们可以看到,拦截的IP调用了一个静态方法,这里的功能很简单,判断数据库中该IP是否存在,如果不存在则新增,如果存在,则更新拦截次数+·1。这里就不多介绍了。这里也没有必要做什么性能优化,反正本来就是拦截的IP,优化个毛。

高性能处理缓存数据

上面我们介绍,我们会另外开启一个进程,维护IP白名单,并且将流量统计提交到数据库。这就是这个进程:

<?php$worker_ip=newWorker();$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_ipas$ip){$ip_md5=md5($ip);$in_length=Cache::pull("$ip_md5-in");//请求的数据$to_length=Cache::pull("$ip_md5-to");//返回的数据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();}}});};

他做的事情很简单,读取缓存,更新数据到数据库,并且更新IP白名单。这里不需要考虑它和数据库之间的性能问题,这是额外的进程,不影响端口的连接和转发。

下一步,更好的性能设计

以上,只有几行代码,几个小时(如果不含设计系统的时间,代码量可能只有一两个小时。还能再怎么优化呢?实际上还是可以优化的。

更好的内存驱动

这里使用的是ThinkPHP内置的文件缓存,存储到磁盘上,以上方法,在大量连接并发时,肯定受制于磁盘的性能。所以自然而然,我们可以使用内存缓存。

版权声明:本文由phpreturn.com(PHP武器库官网)原创和首发,所有权利归phpreturnPHP武器库)所有,本站允许任何形式的转载/引用文章,但必须同时注明出处。

但是使用内存缓存,redis可以吗?并不好。这里是客户端,它只是想简简单单实现一个拦截转发,还要再部署redis,不可取。

但实际上,workerman本身内置了数据共享组件,这是一个很好的方案。相当于一个极简的redis。完美符合我们的需求。但是我并没有实现这个功能,目前的系统已经符合我的场景。

更好的客户端

目前拦截IP客户端和管理面板集成在一起,使用相同的配置,面板基于ThinkPHP,客户端只是ThinkPHP的一个命令。我之所以这样做,是希望直接在Workerman中使用ThinkPHP的众多特性(数据库、缓存

实际上,我们可以将客户端的代码,另外开一个项目,使客户端和面板独立开。在面板上实现通用得API。客户端通过API操作数据。这样客户端就不需要连接数据库。好处多多。

但是这样也带来的更多的工作量,这种情况下,我们自然而然的认为客户端的环境不安全,所以要做权限认证,登录认证。接口开发也要写更多的代码。

以上就是关于“如何用PHP实现一个IP防火墙”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注亿速云行业资讯频道。

本文:如何用PHP实现一个IP防火墙的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:uni-app调取接口的方式及封装uni.request()的方法下一篇:

23 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18