如何在 Symfony2 的请求 subscriber/listener 中检查用户的 IP?

How do I check the User's IP in a Request subscriber/listener in Symfony2?

我的应用程序的某些用户将附加一个 allowedIPs 数组。有一个 guide 用于身份验证 Voter 用于将 IP 列入黑名单,我可以根据身份验证者将用户的 IP 列入白名单。

我在这里看到的问题是用户在一个允许的网络中进行身份验证,然后切换到另一个不允许用户连接的网络。

我认为解决方案是订阅 kernel.request 事件,如果 IP 不被允许,我会取消对用户的授权。

这种对每个请求都检查 IP 的做法是愚蠢的吗?如果不是,我如何在事件订阅者中获得经过身份验证的用户? GetResponseEvent (api docs) 似乎没有提供任何方法来获取经过身份验证的用户(如果存在的话)。

编辑: 正如 Cerad 所建议的那样,我是和一个选民一起做的。

选民class

<?php

namespace My\UserBundle\Security;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Psr\Log\LoggerInterface;
use My\UserBundle\Entity\User;

class ValidClientIpVoter extends AuthenticatedVoter
{
    private $container;
    private $logger;

    public function __construct(ContainerInterface $container, LoggerInterface $logger)
    {
        $this->container = $container;
        $this->logger = $logger;
    }

    public function vote(TokenInterface $token, $object, array $attributes)
    {
        $request = $this->container->get('request');
        $user = $token->getUser();

        // vote on instances of our User class
        if($user instanceof User) {
            $allowed_ips = $user->getAllowedIps();

            // only vote if there actually are limitations
            if(is_array($allowed_ips) && count($allowed_ips)) {
                $this->logger->debug(sprintf('ValidClientIpVoter: Validating allowed IPs for user #%d', $user->getId()));

                // deny access if current request's IP is not allowed for the user
                if(!in_array($request->getClientIp(), $allowed_ips)) {
                    $this->logger->notice(sprintf('ValidClientIpVoter: Invalid client IP for user #%d', $user->getId()));

                    return VoterInterface::ACCESS_DENIED;
                }
            }
        }

        return VoterInterface::ACCESS_ABSTAIN;
    }

}

更改 security.yml 以使投票一致

security:
    access_decision_manager: 
        strategy: unanimous

最后是服务定义

services:
   valid_client_ip_voter:
        class:      My\UserBundle\Security\ValidClientIpVoter
        arguments:  [@service_container, @monolog.logger]
        public:     false
        tags:
            -       { name: security.voter }

要获取当前用户,请将 security.token_storage 服务注入您的侦听器:http://symfony.com/doc/current/book/security.html#retrieving-the-user-object

我实际上会听 kernel.controller 只是为了确保用户可用。

另一方面,选民在每次访问资源时都会被要求,所以我不清楚为什么即使 ip 发生变化,您现有的方法也不起作用。