Documentação do Symfony - versão 3.1
Renderizada do repositório symfony-docs-pt-BR no Github
Imagine que você deseja permitir o acesso ao seu site apenas entre 2pm e 4pm UTC. Antes do Symfony 2.4, você tinha que criar um token personalizado, factory, listener e provedor. Nesse artigo, você vai aprender como fazer isso em um formulário de login (ou seja, onde o usuário submete seu nome de usuário e senha).
New in version 2.4: A interface SimplePreAuthenticatorInterface
foi introduzida no Symfony 2.4.
Primeiro, crie uma nova classe que implementa
SimpleFormAuthenticatorInterface
.
Eventualmente, isso permitirá criar uma lógica personalizada para autenticar
o usuário:
// src/Acme/HelloBundle/Security/TimeAuthenticator.php
namespace Acme\HelloBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class TimeAuthenticator implements SimpleFormAuthenticatorInterface
{
private $encoderFactory;
public function __construct(EncoderFactoryInterface $encoderFactory)
{
$this->encoderFactory = $encoderFactory;
}
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
try {
$user = $userProvider->loadUserByUsername($token->getUsername());
} catch (UsernameNotFoundException $e) {
throw new AuthenticationException('Invalid username or password');
}
$encoder = $this->encoderFactory->getEncoder($user);
$passwordValid = $encoder->isPasswordValid(
$user->getPassword(),
$token->getCredentials(),
$user->getSalt()
);
if ($passwordValid) {
$currentHour = date('G');
if ($currentHour < 14 || $currentHour > 16) {
throw new AuthenticationException(
'You can only log in between 2 and 4!',
100
);
}
return new UsernamePasswordToken(
$user,
$user->getPassword(),
$providerKey,
$user->getRoles()
);
}
throw new AuthenticationException('Invalid username or password');
}
public function supportsToken(TokenInterface $token, $providerKey)
{
return $token instanceof UsernamePasswordToken
&& $token->getProviderKey() === $providerKey;
}
public function createToken(Request $request, $username, $password, $providerKey)
{
return new UsernamePasswordToken($username, $password, $providerKey);
}
}
Excelente! Agora você só precisa configurar algumas Configuração. Mas, primeiro, você pode aprender mais sobre o que cada método faz nessa classe.
Quando Symfony começa a lidar com um pedido, createToken()
é chamado, onde
você cria um objeto TokenInterface
que contém todas as informações que você precisa em authenticateToken()
para autenticar o usuário (por exemplo, o nome de usuário e senha).
Seja qual for o objeto token você criar aqui, ele será passado para você mais tarde em authenticateToken()
.
Após o Symfony chamar createToken()
, ele irá então chamar supportsToken()
em sua classe (e em quaisquer outros listeners de autenticação) para descobrir quem deve
lidar com o token. Esta é apenas uma maneira de permitir que vários mecanismos de autenticação
sejam utilizados para o mesmo firewall (dessa forma, você pode, por exemplo, primeiro tentar
autenticar o usuário via um certificado ou uma chave de API e fall back para
um formulário de login).
Na maioria das vezes, você só precisa ter certeza de que esse método retorna true
para um
token que foi criado por createToken()
. Sua lógica provavelmente deve parecer
exatamente como neste exemplo.
Se supportsToken
retornar true
, o Symfony irá chamar agora authenticateToken()
.
Seu trabalho aqui é verificar se é permitido o login do token primeiro
obtendo o objeto User
através do provedor de usuário e, em seguida, verificando a senha
e a hora atual.
Note
O “fluxo” de como obter o objeto User
e determinar se o token é ou não
válido (por exemplo, verificar a senha), pode variar de acordo com os seus
requisitos.
Por fim, o seu trabalho é retornar um objeto token novo que é “autenticado”
(ou seja, ele tem pelo menos um papel - role - definido) e que tem o objeto User
em seu interior.
Dentro deste método, um encoder é necessário para verificar a validade da senha:
$encoder = $this->encoderFactory->getEncoder($user);
$passwordValid = $encoder->isPasswordValid(
$user->getPassword(),
$token->getCredentials(),
$user->getSalt()
);
Esse é um serviço que já está disponível no Symfony e o algoritmo de senha
está definido na configuração de segurança (por exemplo, security.yml
) sob
a chave encoders
. Abaixo, você verá como injetar isso no TimeAuthenticator
.
Agora, configure o seu TimeAuthenticator
como um serviço:
1 2 3 4 5 6 7 | # app/config/config.yml
services:
# ...
time_authenticator:
class: Acme\HelloBundle\Security\TimeAuthenticator
arguments: ["@security.encoder_factory"]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <!-- app/config/config.xml -->
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<!-- ... -->
<service id="time_authenticator"
class="Acme\HelloBundle\Security\TimeAuthenticator"
>
<argument type="service" id="security.encoder_factory" />
</service>
</services>
</container>
|
1 2 3 4 5 6 7 8 9 10 | // app/config/config.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
// ...
$container->setDefinition('time_authenticator', new Definition(
'Acme\HelloBundle\Security\TimeAuthenticator',
array(new Reference('security.encoder_factory'))
));
|
Em seguida, ative-o na seção firewalls
da configuração de segurança
utilizando a chave simple_form
:
1 2 3 4 5 6 7 8 9 10 11 12 | # app/config/security.yml
security:
# ...
firewalls:
secured_area:
pattern: ^/admin
# ...
simple_form:
authenticator: time_authenticator
check_path: login_check
login_path: login
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <!-- app/config/security.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<srv:container xmlns="http://symfony.com/schema/dic/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:srv="http://symfony.com/schema/dic/services"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd">
<config>
<!-- ... -->
<firewall name="secured_area"
pattern="^/admin"
>
<simple-form authenticator="time_authenticator"
check-path="login_check"
login-path="login"
/>
</firewall>
</config>
</srv:container>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // app/config/security.php
// ..
$container->loadFromExtension('security', array(
'firewalls' => array(
'secured_area' => array(
'pattern' => '^/admin',
'simple_form' => array(
'provider' => ...,
'authenticator' => 'time_authenticator',
'check_path' => 'login_check',
'login_path' => 'login',
),
),
),
));
|
A chave simple_form
tem as mesmas opções que a opção normal form_login
, mas com a chave adicional authenticator
que aponta para o
novo serviço. Para mais detalhes, veja reference-security-firewall-form-login.
Se criar um formulário de login em geral é novidade para você ou se não entende
as opções check_path
ou login_path
, veja /cookbook/security/form_login.