Documentação do Symfony - versão 3.1
Renderizada do repositório symfony-docs-pt-BR no Github
O componente de segurança do Symfony2 fornece várias camadas para autorizar usuários. Uma das camadas é chamada “voter”. Um voter é uma classe dedicada que verifica se o usuário possui direitos para conectar-se à aplicação ou para acessar um recurso/URL específico. Por exemplo, o Symfony2 fornece uma camada que verifica se o usuário está totalmente autorizado ou se ele tem alguns papéis esperados.
Às vezes é útil criar um voter personalizado para lidar com um caso específico não manipulado pelo framework. Nesta seção, você vai aprender como criar um voter que permitirá criar uma blacklist de usuários por seus IPs.
Um voter personalizado deve implementar
VoterInterface
,
que exige os três métodos a seguir:
Neste exemplo, você vai verificar se o endereço IP do usuário corresponde a uma lista de
endereços na blacklist e “algo” será a aplicação. Se o IP do usuário está na lista negra, você vai retornar
VoterInterface::ACCESS_DENIED
, caso contrário você vai retornar
VoterInterface::ACCESS_ABSTAIN
pois o propósito desse voter é somente negar
acesso, e não conceder.
Para adicionar um usuário na lista negra com base em seu endereço IP, você pode usar o serviço request
e comparar o endereço IP a um conjunto de endereços IP na lista negra:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | // src/Acme/DemoBundle/Security/Authorization/Voter/ClientIpVoter.php
namespace Acme\DemoBundle\Security\Authorization\Voter;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class ClientIpVoter implements VoterInterface
{
protected $requestStack;
private $blacklistedIp;
public function __construct(RequestStack $requestStack, array $blacklistedIp = array())
{
$this->requestStack = $requestStack;
$this->blacklistedIp = $blacklistedIp;
}
public function supportsAttribute($attribute)
{
// you won't check against a user attribute, so return true
return true;
}
public function supportsClass($class)
{
// your voter supports all type of token classes, so return true
return true;
}
public function vote(TokenInterface $token, $object, array $attributes)
{
$request = $this->requestStack->getCurrentRequest();
if (in_array($request->getClientIp(), $this->blacklistedIp)) {
return VoterInterface::ACCESS_DENIED;
}
return VoterInterface::ACCESS_ABSTAIN;
}
}
|
É isso! O voter está pronto. O próximo passo é injetar o voter na camada de segurança. Isso pode ser feito facilmente através do container de serviço.
Tip
A sua implementação dos métodos
supportsAttribute()
e supportsClass()
não está sendo chamada internamente pelo framework. Depois de ter registado o seu
voter o método vote()
sempre será chamado, independentemente desses dois
métodos retornarem true ou não. Portanto, você precisa chamar os
métodos na sua implementação do método vote()
e retornar ACCESS_ABSTAIN
se o seu voter não suporta a classe ou atributo.
Para injetar o voter na camada de segurança, você deve declará-lo como um serviço,
e adicionar a tag security.voter
:
1 2 3 4 5 6 7 8 | # src/Acme/AcmeBundle/Resources/config/services.yml
services:
security.access.blacklist_voter:
class: Acme\DemoBundle\Security\Authorization\Voter\ClientIpVoter
arguments: ["@request_stack", [123.123.123.123, 171.171.171.171]]
public: false
tags:
- { name: security.voter }
|
1 2 3 4 5 6 7 8 9 10 | <!-- 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="request_stack" strict="false" />
<argument type="collection">
<argument>123.123.123.123</argument>
<argument>171.171.171.171</argument>
</argument>
<tag name="security.voter" />
</service>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 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('request_stack'),
array('123.123.123.123', '171.171.171.171'),
),
);
$definition->addTag('security.voter');
$definition->setPublic(false);
$container->setDefinition('security.access.blacklist_voter', $definition);
|
Tip
Certifique-se de importar esse arquivo de configuração em seu arquivo de configuração principal
da aplicação (por exemplo, app/config/config.yml
). Para mais informações
veja Importando configuração com imports. Para ler mais sobre a definição de
serviços em geral, consulte o capítulo /book/service_container .
Para que o novo voter tenha efeito, é necessário alterar a estratégia de decisão de acesso padrão, que, concede acesso se qualquer voter conceder acesso.
Neste caso, escolha a estratégia unanimous
. Ao contrário da estratégia
affirmative
(o padrão), com a estratégia unanimous
, se apenas um voter
negar o acesso (por exemplo, o ClientIpVoter
), o acesso não é concedido ao
o usuário final.
Para fazer isso, sobrescreva a seção access_decision_manager
padrão de seu
arquivo de configuração da aplicação com o seguinte código.
1 2 3 4 5 | # app/config/security.yml
security:
access_decision_manager:
# strategy can be: affirmative, unanimous or consensus
strategy: unanimous
|
1 2 3 4 5 | <!-- app/config/security.xml -->
<config>
<!-- strategy can be: affirmative, unanimous or consensus -->
<access-decision-manager strategy="unanimous">
</config>
|
1 2 3 4 5 6 7 | // app/config/security.xml
$container->loadFromExtension('security', array(
// strategy can be: affirmative, unanimous or consensus
'access_decision_manager' => array(
'strategy' => 'unanimous',
),
));
|
É isso! Agora, no momento de decidir se um usuário deve ou não ter acesso, o novo voter vai negar acesso a qualquer usuário na lista negra de IPs.
See also
Para um uso mais avançado ver components-security-access-decision-manager.