IPアドレスのブラックリストの独自 Voter の実装方法

Symfony2 のセキュリティコンポーネントは、ユーザ認証のための複数のレイヤーを用意しています。 voter と呼ばれるレイヤーは、その1つです。 voter はユーザがアプリケーションに接続できる権利があるかのチェックを行うクラスです。例えば、 Symfony2 は、ユーザが完全に認証されているか、また、必要な権限を保持しているかといったことをチェックするレイヤーを提供します。

フレームワークによる処理ではなく、特定のケースを処理するためのカスタム化された voter が役に立つこともあります。このセクションでは、 IP アドレスに基づきユーザをブラックリストに入れるための voter の作り方を学びましょう

Voter インタフェース

カスタム Voter は、次の3つのメソッドを必要とする Symfony\Component\Security\Core\Authorization\Voter\VoterInterface インタフェースを実装する必要があります:

interface VoterInterface
{
    function supportsAttribute($attribute);
    function supportsClass($class);
    function vote(TokenInterface $token, $object, array $attributes);
}

supportsAttribute() メソッドは、 voter が権限や ACL といったユーザの属性をサポートするかチェックします。

supportsClass() メソッドは、 voter が現在のユーザのトークンクラスをサポートするかチェックします。

vote() メソッドは、ビジネスロジックを実装する必要があり、そこでユーザがアクセス可能か証明します。このメソッドは、次の値のいずれかを返す必要があります。

  • VoterInterface::ACCESS_GRANTED: ユーザがアプリケーションにアクセス可能である
  • VoterInterface::ACCESS_ABSTAIN: voter では、 ユーザがアクセス可能か否かが判断できない
  • VoterInterface::ACCESS_DENIED: ユーザがアプリケーションにアクセス不可能である

この例では、ユーザの IP アドレスがブラックリストのアドレス群を調べます。ユーザの IP がブラックリスト内にあれば、 VoterInterface::ACCESS_DENIED を返し、そうでなければ、 VoterInterface::ACCESS_ABSTAIN を返します。つまり、この voter の目的は、実際のアクセスを与えることではなく、アクセスの拒否のみとします。

カスタム Voter の作成方法

ユーザを IP に基づいてブラックリストに入れるには、 request サービスを使用してブラックリストの IP アドレスの集合と比較します。

namespace Acme\DemoBundle\Security\Authorization\Voter;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

class ClientIpVoter implements VoterInterface
{
    public function __construct(ContainerInterface $container, array $blacklistedIp = array())
    {
        $this->container     = $container;
        $this->blacklistedIp = $blacklistedIp;
    }

    public function supportsAttribute($attribute)
    {
        // ユーザ属性は調べないので、 true を返します
        return true;
    }

    public function supportsClass($class)
    {
        // voter はトークンクラスの全てをサポートするので、 true を返します
        return true;
    }

    function vote(TokenInterface $token, $object, array $attributes)
    {
        $request = $this->container->get('request');
        if (in_array($this->request->getClientIp(), $this->blacklistedIp)) {
            return VoterInterface::ACCESS_DENIED;
        }

        return VoterInterface::ACCESS_ABSTAIN;
    }
}

これで voter ができました。次のステップは、 voter をセキュリティレイヤーに注入する(inject)ことです。これは、サービスコンテナを介して簡単に行うことができます。

Voter をサービスとして宣言する

Voter をセキュリティレイヤーに注入する(inject)には、 Voter をサービスとして宣言して、 “security.voter” としてタグ付けする必要があります。

  • YAML
    # src/Acme/AcmeBundle/Resources/config/services.yml
    
    services:
        security.access.blacklist_voter:
            class:      Acme\DemoBundle\Security\Authorization\Voter\ClientIpVoter
            arguments:  [@service_container, [123.123.123.123, 171.171.171.171]]
            public:     false
            tags:
                -       { name: security.voter }
  • XML
    <!-- src/Acme/AcmeBundle/Resources/config/services.xml -->
    
    <service id="security.access.blacklist_voter"
             class="Acme\DemoBundle\Security\Authorization\Voter\ClientIpVoter" public="false">
        <argument type="service" id="service_container" strict="false" />
        <argument type="collection">
            <argument>123.123.123.123</argument>
            <argument>171.171.171.171</argument>
        </argument>
        <tag name="security.voter" />
    </service>
    
  • PHP
    // src/Acme/AcmeBundle/Resources/config/services.php
    
    use Symfony\Component\DependencyInjection\Definition;
    use Symfony\Component\DependencyInjection\Reference;
    
    $definition = new Definition(
        'Acme\DemoBundle\Security\Authorization\Voter\ClientIpVoter',
        array(
            new Reference('service_container'),
            array('123.123.123.123', '171.171.171.171'),
        ),
    );
    $definition->addTag('security.voter');
    $definition->setPublic(false);
    
    $container->setDefinition('security.access.blacklist_voter', $definition);
    

Tip

このコンフィギュレーションファイルをメインのアプリケーションファイル( app/config/config.yml など)からインポートすることを忘れないでください。詳細は、 imports を使ってコンフィギュレーションをインポートする を参照してください。より一般的なサービスの定義については、ドキュメントの サービスコンテナ 章を参照してください。

アクセス可否の決定戦略を変更する

新しい voter の効力を有効にするために、デフォルトのアクセス決定戦略を変更する必要があります。デフォルトでは、 いずれかの voter がアクセスを許可していれば、良いことになっています。

ここでのケースでは、 unanimous 戦略を選択しましょう。デフォルトの affirmative 戦略と異なり、 unanimous 戦略は voter 1つでもアクセスを拒否すれば(例えば ClientIpVoter )、ユーザにアクセスが許可されません。

アプリケーションのコンフィギュレーションファイルの access_decision_manager セクションをデフォルト値から次のようにオーバーライドしましょう。

  • YAML
    # app/config/security.yml
    security:
        access_decision_manager:
            # Strategy can be: affirmative, unanimous or consensus
            strategy: unanimous
    

できました。これで、ユーザがアクセスがあるかどうかを決定する歳に、新しい Voter はブラックリスト IP リストに入っているユーザを全てアクセス拒否するようになりました。

このページのコンテンツ

前のドキュメント

新しいリクエストのフォーマットとマイムタイプの登録方法

次のドキュメント

既にあるデータベースからエンティティを生成する方法

ソース



クイックリンク

コメントリスト


ご質問や翻訳不備等お気軽にコメントください。


現在、翻訳が古くなっている箇所が多くあります。1箇所、1行などほんの少量でもかまいませんので、ドキュメント翻訳にご協力いただける方を募集しています。日本 Symfony ユーザー会メーリングリストまでご連絡ください。