如何在Symfony 5中将对网站的访问限制为特定国家(地理封锁)

本文概述

  • 1.下载GeoLite2免费数据库
  • 2.安装MaxMind GeoIP2 PHP API
  • 3.创建请求监听器
  • 4.在services.yaml文件上注册事件监听器
一些应用程序管理员选择根据用户的位置拒绝对用户的访问。这被称为地理封锁, 例如, 它可以在某些购物网站上使用, 这些网站可能选择不接待来自不向其运送商品的国家/地区的访问者。如果你愿意在Symfony 5项目中实现此功能, 那么你找到了正确的位置。
本教程遵循上一篇文章的一些步骤, 其中我们解释了如何从Symfony 3中的访问者IP中检测城市, 国家和地区, 但进行了修改, 即应限制对整个网站的访问。
1.下载GeoLite2免费数据库 第一步, 你将需要项目中或在系统级别可访问的GeoLite二进制数据库。你可以从此处免费从官方的MaxMind GeoLite2下载数据库。在此页面中, 你需要注册一个GeoLite2帐户:
如何在Symfony 5中将对网站的访问限制为特定国家(地理封锁)

文章图片
创建帐户并按照通过电子邮件收到的步骤操作后, 你将可以私下下载MaxMind数据库, 在这种情况下, 我们将使用GeoLite2国家/地区版本:
如何在Symfony 5中将对网站的访问限制为特定国家(地理封锁)

文章图片
在本教程中, 我们将使用数据库并将其包含在我们自己的项目中的symfony项目的/ private目录中(请注意, 该目录不存在, 因此需要创建, 你可以根据以下内容更改数据库的路径:你的需求), 我们将使用数据库的国家/地区版本, 使我们能够获取本文中提到的信息, 特别是访问者IP的国家/地区。数据库是使用tar压缩的, 因此你可以使用以下命令从命令行提取其内容:
tar -xzf GeoLite2-Country_20200121.tar.gz

另外, 在Windows等其他操作系统中, 你也可以使用7Zip, Winrar或其他解压缩工具来提取其内容。现在你的项目中已有数据库, 目录结构应如下所示:
project/├── bin├── composer.json├── composer.lock├── config├── nbproject├── phpunit.xml.dist├── private/│└── geolite2-country/│├── COPYRIGHT.txt│├── GeoLite2-Country.mmdb│└── LICENSE.txt├── public├── src├── symfony.lock├── templates├── tests├── translations├── var└── vendor

2.安装MaxMind GeoIP2 PHP API 为了读取数据库, 你不需要托管在MySQL或其他数据库管理器中。数据库具有来自GeoIP的创建者的特殊格式, 即MaxMind DB。 MaxMind DB文件格式是一种数据库格式, 可以使用有效的二进制搜索树将IPv4和IPv6地址映射到数据记录。二进制数据库分为三个部分:
  1. 二进制搜索树。树的每个级别对应于IPv6地址的128位表示形式中的单个位。
  2. 数据部分。这些是针对特定IP地址返回给客户端的值, 例如” 美国” , “ 纽约” 或由多个字段组成的更复杂的地图类型。
  3. 数据库元数据。有关数据库本身的信息。
有关此项目使用的数据库类型的更多信息, 请在此处阅读有关MaxMind DB的更多信息。
现在, 我们需要一种用于这种数据库格式的解析器。幸运的是, MaxMind团队为PHP编写了一个很棒的库, 该库使与数据库的交互变得非常容易, 并且你仅需几行代码就可以检索有关用户IP的地理信息。我们正在谈论的是MaxMind GeoIP PHP Api, 该软件包为GeoIP2 Web服务和数据库提供了API。该API还可以与免费的GeoLite2数据库(我们正在使用的数据库)一起使用。你可以使用运行以下命令的composer将此软件包安装在Symfony项目中:
composer require geoip2/geoip2

有关此库的更多信息, 请访问Github上的官方存储库。安装软件包后, 你将可以在Symfony的控制器上使用其类。
3.创建请求监听器 在每个Symfony应用程序上, 很多事情发生在幕后, 因此我们需要知道什么时候发生。 Symfony通过事件通知你何时发生这种情况, 它在处理HTTP请求时会触发与内核相关的多个事件。这正是我们要识别用户所在国家/地区并确定他是否应具有访问权限的地方。逻辑将如下所示, 在项目源的EventListener目录内创建一个RequestListener类。此类仅在构造函数中接收2个参数, 第一个是可以使用services.yaml文件中的%kernel.project_dir%变量注入到该类中的项目的绝对路径。第二个参数是模板引擎(Twig), 因此我们可以呈现一个视图, 该视图将通知用户该网站已被阻止。
此外, 该类将具有一个私有数组变量, 该变量将包含无法访问网站的国家/地区的ISO代码, 你可以根据自己的需要进行更新, 在本例中, 我们将仅阻止4个国家/地区:
  • 哥伦比亚
  • 巴西
  • 玻利维亚
  • 美国
侦听器类将响应Symfony的onKernelRequest事件, 并将在运行时调用RestrictAccessOnDisallowedCountries方法, 并提供RequestEvent变量作为第一个参数。提到的方法将读取GeoLite2数据库, 并检查用户IP的国家/地区。如果有结果, 它将获取国家/地区的ISO代码, 并会检查访客代码是否在黑名单中;如果存在, 则会使用自定义的Twig视图发送新的响应, 其中包含针对用户的消息(请注意, 响应代码将为” 403禁止” ):
< ?php// src/EventListener/RequestListener.phpnamespace App\EventListener; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpFoundation\Response; use Twig\Environment; // 1. Include GeoIp2 Classesuse GeoIp2\Database\Reader; use GeoIp2\Exception\AddressNotFoundException; class RequestListener{// Store the absolute path of the project injected through the serviceprivate $projectDir; /* @var $twig \Twig\Environment */private $twig; /*** An array with all the ISO codes of the countries where the website shouldn't be accessible* * @var Array*/private $blacklist = ["CO", // Colombia"BO", // Bolivia"BR", // Brazil"US", // United States]; public function __construct(Environment $twigEnvironment, $projectDir){$this-> projectDir = $projectDir; $this-> twig = $twigEnvironment; }/*** Run the verification of the users country on every request.* * @param RequestEvent $event* @return type*/public function onKernelRequest(RequestEvent $event){if (!$event-> isMasterRequest()) {// don't do anything if it's not the master requestreturn; }$this-> RestrictAccessOnDisallowedCountries($event); }private function RestrictAccessOnDisallowedCountries(RequestEvent $event){/* @var $request \Symfony\Component\HttpFoundation\Request */$request = $event-> getRequest(); // Declare the path to the GeoLite2-City.mmdb file (database)$GeoLiteDatabasePath = $this-> projectDir . '/private/geolite2-country/GeoLite2-Country.mmdb'; // Create an instance of the Reader of GeoIp2 and provide as first argument// the path to the database file$reader = new Reader($GeoLiteDatabasePath); // Check against the GeoLite database the user's country through his IPtry{// You can as well test with a fixed IP, for example one of USA in Minessota://$reader-> country('128.101.101.101'); // However for production, request the client ip://$reader-> country($request-> getClientIp()); /* @var $record \GeoIp2\Model\Country */$record = $reader-> country($request-> getClientIp()); $isoCode = $record-> country-> isoCode; // If the obtained iso code matches with one of the blacklisted countries, block the access// rendering a custom pageif(in_array($isoCode, $this-> blacklist)){$response = new Response(); $response-> setStatusCode(Response::HTTP_FORBIDDEN); // Render some twig view, in our case we will render the blocked.html.twig file$response-> setContent($this-> twig-> render("pages/blocked.html.twig", ['code' => $isoCode])); // Return an HTML file$response-> headers-> set('Content-Type', 'text/html'); // Send response$event-> setResponse($response); }} catch (AddressNotFoundException $ex) {// Couldn't retrieve geo information from the given IP// Is up to you if you want to block the access to the website anyway here ...}}}

请注意, 如果无法确定访问者所在的国家/地区, 此方法不会执行任何操作。如你所见, 在代码中, 我们呈现了以下Twig视图:
{# application/templates/pages/blocked.html.twig #}< !DOCTYPE html> < html> < head> < meta charset="UTF-8"> < title> Website Blocked< /title> {% block stylesheets %}{% endblock %}< /head> < body> < h1> Access Disallowed< /h1> < p> This website doesn't work in your country ({{ code }})< /p> < /body> < /html>

4.在services.yaml文件上注册事件监听器 最后, 为了启用事件监听器, 与自动装配的服务不??同, 你将需要在services.yaml文件上注册事件监听器, 如下所示:
# /application/config/services.yamlservices:App\EventListener\RequestListener:tags:- { name: kernel.event_listener, event: kernel.request }bind:$projectDir: '%kernel.project_dir%'

如步骤3所述, 我们将项目目录绑定为参数。就是这样!清除项目的缓存, 然后尝试通过VPN访问你的应用程序, 或为侦听器手动设置固定的ip, 然后检查地理封锁是否正常工作。
【如何在Symfony 5中将对网站的访问限制为特定国家(地理封锁)】快乐编码!

    推荐阅读