it-swarm-pt.tech

Autenticação de usuário pós-registro automático

Estamos construindo um aplicativo de negócios a partir do zero no Symfony 2, e eu me deparo com um obstáculo com o fluxo de registro do usuário: depois que o usuário cria uma conta, ele deve estar automaticamente logado com essas credenciais, de ser imediatamente forçado a fornecer suas credenciais novamente.

Alguém teve alguma experiência com isso, ou capaz de me apontar na direção certa?

106
Problematic

Symfony 4.0

Este processo não mudou do symfony 3 para o 4, mas aqui está um exemplo usando o recém-recomendado AbstractController. Os serviços security.token_storage e session são registrados no método pai getSubscribedServices, portanto, você não precisa adicioná-los ao controlador.

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use YourNameSpace\UserBundle\Entity\User;

class LoginController extends AbstractController{

    public function registerAction()
    {    
        $user = //Handle getting or creating the user entity likely with a posted form
        $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
        $this->container->get('security.token_storage')->setToken($token);
        $this->container->get('session')->set('_security_main', serialize($token));
        // The user is now logged in, you can redirect or do whatever.
    }

}

Symfony 2.6.x - Symfony 3.0.x

A partir do symfony 2.6 security.context está obsoleto em favor de security.token_storage. O controlador pode agora ser simplesmente:

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use YourNameSpace\UserBundle\Entity\User;

class LoginController extends Controller{

    public function registerAction()
    {    
        $user = //Handle getting or creating the user entity likely with a posted form
        $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
        $this->get('security.token_storage')->setToken($token);
        $this->get('session')->set('_security_main', serialize($token));
    }

}

Embora isso esteja obsoleto, você ainda pode usar security.context, pois ele foi feito para ser compatível com versões anteriores. Esteja pronto para atualizá-lo para o Symfony 3

Você pode ler mais sobre as mudanças 2.6 para segurança aqui: https://github.com/symfony/symfony/blob/2.6/UPGRADE-2.6.md

Symfony 2.3.x

Para realizar isso no symfony 2.3, você não pode mais apenas definir o token no contexto de segurança. Você também precisa salvar o token para a sessão.

Assumindo um arquivo de segurança com um firewall como:

// app/config/security.yml
security:
    firewalls:
        main:
            //firewall settings here

E uma ação do controlador similar também:

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use YourNameSpace\UserBundle\Entity\User;

class LoginController extends Controller{

    public function registerAction()
    {    
        $user = //Handle getting or creating the user entity likely with a posted form
        $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
        $this->get('security.context')->setToken($token);
        $this->get('session')->set('_security_main',serialize($token));
        //Now you can redirect where ever you need and the user will be logged in
    }

}

Para a criação do token, você desejará criar uma UsernamePasswordToken. Isso aceita 4 parâmetros: User Entity, User Credentials, Firewall Name, User Roles. Você não precisa fornecer as credenciais do usuário para que o token seja válido.

Não tenho 100% de certeza de que definir o token no security.context é necessário se você for redirecionar imediatamente. Mas não parece doer, então eu deixei.

Então a parte importante, definindo a variável de sessão. A convenção de nomenclatura de variáveis ​​é _security_ seguida do nome do seu firewall, neste caso main tornando _security_main

138
Chase

Descobri isso finalmente.

Após o registro do usuário, você deve ter acesso a uma instância de objeto do que você definiu como sua entidade de usuário na configuração do provedor. A solução é criar um novo token com essa entidade de usuário e passá-lo para o contexto de segurança. Aqui está um exemplo baseado na minha configuração:

RegistrationController.php:

$token = new UsernamePasswordToken($userEntity, null, 'main', array('ROLE_USER'));
$this->get('security.context')->setToken($token);

Onde main é o nome do firewall da sua aplicação (obrigado, @Joe). Isso é realmente tudo que existe para isso; O sistema agora considera seu usuário totalmente logado como o usuário que acabou de criar.

EDIT: Por @ Miquel comentário, eu atualizei o exemplo de código do controlador para incluir uma função padrão sensata para um novo usuário (embora, obviamente, isso pode ser ajustado de acordo com as necessidades específicas do aplicativo).

65
Problematic

Estou usando o Symfony 2.2 e minha experiência foi um pouco diferente de Problematic's , então esta é uma versão combinada de todas as informações desta questão, além de algumas das minhas. 

Eu acho que Joe está errado sobre o valor de $providerKey, o terceiro parâmetro para o construtor UsernamePasswordToken. É suposto ser a chave de um provedor de autenticação (não de usuário). Ele é usado pelo sistema de autenticação para distinguir entre os tokens criados para diferentes provedores. Qualquer provedor que desça de UserAuthenticationProvider só autenticará tokens cuja chave de provedor corresponde à sua própria. Por exemplo, o UsernamePasswordFormAuthenticationListener define a chave do token que cria para corresponder ao seu correspondente DaoAuthenticationProvider . Isso permite que um único firewall tenha vários provedores de nome de usuário + senha, sem que eles pisem um no outro. Portanto, precisamos escolher uma chave que não entre em conflito com outros provedores. Eu uso 'new_user'.

Eu tenho alguns sistemas em outras partes do meu aplicativo que dependem do evento de sucesso authentication , e isso não é acionado apenas configurando o token no contexto. Eu tive que pegar o EventDispatcher do container e disparar o evento manualmente. Decidi também não disparar um evento de login interativo porque estamos autenticando o usuário implicitamente, não em resposta a uma solicitação de login explícita.

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\AuthenticationEvents;
use Symfony\Component\Security\Core\Event\AuthenticationEvent;

$user = // get a Symfony user instance somehow
$token = new UsernamePasswordToken(
        $user, null, 'new_user', $user->getRoles() );
$this->get( 'security.context' )->setToken( $token );
$this->get( 'event_dispatcher' )->dispatch(
        AuthenticationEvents::AUTHENTICATION_SUCCESS,
        new AuthenticationEvent( $token ) );

Observe que o uso de $this->get( .. ) pressupõe que o snippet esteja em um método do controlador. Se você estiver usando o código em outro lugar, você terá que alterá-los para chamar ContainerInterface::get( ... ) de maneira apropriada ao ambiente. Como acontece, minhas entidades de usuário implementam UserInterface para que eu possa usá-las diretamente com o token. Se você não precisa encontrar uma maneira de convertê-los em instâncias UserInterface.

Esse código funciona, mas eu sinto que ele está invadindo a arquitetura de autenticação do Symfony em vez de trabalhar com ele. Provavelmente seria mais correto implementar um novo provedor de autenticação com sua própria classe de token em vez de seqüestrar a UsernamePasswordToken. Além disso, usar um provedor adequado significaria que os eventos foram tratados para você.

6
Sam Hanes

Se você tiver um objeto UserInterface (e esse deve ser o caso na maioria das vezes), você pode querer usar a função getRoles que implementa para o último argumento. Então, se você criar uma função logUser, ela deve parecer Curtiu isso:

public function logUser(UserInterface $user) {
    $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
    $this->container->get('security.context')->setToken($token);
}
6
Cédric Nirousset

No caso de alguém ter a mesma pergunta que me fez voltar para cá:

Chamando 

$this->container->get('security.context')->setToken($token); 

afeta apenas o security.context atual para a rota usada. 

Ou seja você só pode fazer login em um usuário a partir de um URL dentro do controle do firewall.

(Adicione uma exceção para a rota, se necessário - IS_AUTHENTICATED_ANONYMOUSLY)

4
daemonl

Como problemático aqui já mencionado, este parâmetro $ providerKey indescritível não é na realidade nada mais que o nome da sua regra de firewall, 'foobar' no caso do exemplo abaixo.

firewalls:
    foobar:
        pattern:    /foo/
2
Nim

Eu tentei todas as respostas aqui e nenhuma funcionou. A única maneira de autenticar meus usuários em um controlador é fazer uma subrequest e, em seguida, redirecionar. Aqui está o meu código, estou usando o silex, mas você pode adaptá-lo facilmente ao symfony2:

$subRequest = Request::create($app['url_generator']->generate('login_check'), 'POST', array('_username' => $email, '_password' => $password, $request->cookies->all(), array(), $request->server->all());

$response = $app->handle($subRequest, HttpKernelInterface::MASTER_REQUEST, false);

return $app->redirect($app['url_generator']->generate('curriculos.editar'));
2
Diego Castro

No Symfony versão 2.8.11 (provavelmente trabalhando para versões antigas e mais novas), se você usar o FOSUserBundle simplesmente faça isto:

try {
    $this->container->get('fos_user.security.login_manager')->loginUser(
    $this->container->getParameter('fos_user.firewall_name'), $user, null);
} catch (AccountStatusException $ex) {
    // We simply do not authenticate users which do not pass the user
    // checker (not enabled, expired, etc.).
}

Não há necessidade de despachar evento como já vi em outras soluções.

inpired de FOS\UserBundle\Controller\RegistrationController :: authenticateUser

(do composer.json versão do FOSUserBundle: "friendsofsymfony/user-bundle": "~ 1.3")

0
Nico