个性化阅读
专注于IT技术分析

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

本文概述

一些应用程序管理员选择根据用户的位置拒绝对用户的访问。这被称为地理封锁, 例如, 它可以在某些购物网站上使用, 这些网站可能选择不接待来自不向其运送商品的国家/地区的访问者。如果你愿意在Symfony 5项目中实现此功能, 那么你找到了正确的位置。

本教程遵循上一篇文章的一些步骤, 其中我们解释了如何从Symfony 3中的访问者IP中检测城市, 国家和地区, 但进行了修改, 即应限制对整个网站的访问。

1.下载GeoLite2免费数据库

第一步, 你将需要项目中或在系统级别可访问的GeoLite二进制数据库。你可以从此处免费从官方的MaxMind GeoLite2下载数据库。在此页面中, 你需要注册一个GeoLite2帐户:

GeoLite2注册MaxMin

创建帐户并按照通过电子邮件收到的步骤操作后, 你将可以私下下载MaxMind数据库, 在这种情况下, 我们将使用GeoLite2国家/地区版本:

GeoLite数据库

在本教程中, 我们将使用数据库并将其包含在我们自己的项目中的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.php
namespace App\EventListener;

use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpFoundation\Response;
use Twig\Environment;

// 1. Include GeoIp2 Classes
use GeoIp2\Database\Reader;
use GeoIp2\Exception\AddressNotFoundException;


class RequestListener
{
    // Store the absolute path of the project injected through the service
    private $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 request
            return;
        }
        
        
        $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 IP
        try{
            // 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 page
            if(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.yaml
services:
    App\EventListener\RequestListener:
        tags:
            - { name: kernel.event_listener, event: kernel.request }
        bind:
            $projectDir: '%kernel.project_dir%'

如步骤3所述, 我们将项目目录绑定为参数。就是这样!清除项目的缓存, 然后尝试通过VPN访问你的应用程序, 或为侦听器手动设置固定的ip, 然后检查地理封锁是否正常工作。

快乐编码!

赞(0)
未经允许不得转载:srcmini » 如何在Symfony 5中将对网站的访问限制为特定国家(地理封锁)

评论 抢沙发

评论前必须登录!