如何在 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 发生变化,您现有的方法也不起作用。
我的应用程序的某些用户将附加一个 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 发生变化,您现有的方法也不起作用。