2
respostas

Duvida sobre autenticacao no symfony

Estou tentando implementar um cenario um pouco mais complexo de autenticacao utilizando symfony. Onde eu tenho uma entidade 'papel' que pode ter varios usuarios, nessa entidade papel vao ficar as roles que os usuarios associados a ela podem acessar. Ate ai tudo bem, o problema foi quando eu tentei pesquisar sobre como reescrever o metodo padrao de autenticacao do symfony. Por que eu preciso que ele pare de olhar as roles do usuario, a minha ideia e a seguinte: O usuario faz login e eh redirecionado para uma tela onde ele podera escolher com qual papel logar e entao armazenar na sessao do symfony/cache que ele esta logado com aquele papel. Para verificar as permissoes em cada request um event listener utilizando kernel.request resolve. Porem o unico problema e que eu nao consegui encontrar exemplo em lugar nenhum nem na documentacao do symfony de como reescrever a parte de security onde eu paro de olhar as roles na entidade de usuario. O chatgpt me deu essa ideia:

namespace App\Security;

use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator; use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;

class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface { // ...

public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey)
{
    // Obtenha o usuário logado
    $user = $token->getUser();

    // Liste os papeis do usuário
    $papeis = $user->getPapeis();

    // Redirecione para uma página onde o usuário pode selecionar um papel
    return new RedirectResponse($this->router->generate('selecionar_papel'));
}

// ...

}

porem precisaria usar um meta atributo para o symfony reeconhecer ou registrar no servico, porem como nao localizei nada na documentacao, nao sei qual meta atributo usar ou como registrar no service. Se alguem sabe como fazer isso ou sabe onde fica alguma documentacao com isso no symfony vai me ajudar bastante.

2 respostas

Olá Vitor, tudo bem?

Primeiro, você está no caminho certo ao estender a classe AbstractFormLoginAuthenticator e implementar o método onAuthenticationSuccess. Isso permite que você redirecione o usuário para a página de seleção de papel após o login.

Para registrar o seu LoginFormAuthenticator como um serviço no Symfony, você precisa definir o serviço no arquivo de configuração do Symfony, geralmente services.yaml. Aqui está um exemplo de como fazer isso:

# config/services.yaml
services:
    App\Security\LoginFormAuthenticator:
        arguments:
            $router: '@router'
        tags:
            - { name: 'monolog.logger', channel: 'security' }
            - { name: 'security.guard.authenticator' }

No exemplo acima, estamos registrando o LoginFormAuthenticator como um serviço e injetando o roteador ($router). A tag security.guard.authenticator é usada para informar ao Symfony que este serviço é um autenticador.

Além disso, você precisa configurar o seu firewall para usar o seu autenticador personalizado. Isso é feito no arquivo de configuração de segurança, geralmente security.yaml:

# config/packages/security.yaml
security:
    firewalls:
        main:
            guard:
                authenticators:
                    - App\Security\LoginFormAuthenticator
            # outras configurações do firewall...

Com isso, o Symfony saberá que deve usar o seu LoginFormAuthenticator para autenticação.

Agora, para armazenar o papel selecionado na sessão, você pode usar o serviço de sessão do Symfony. Aqui está um exemplo de como você pode fazer isso dentro do seu LoginFormAuthenticator:

namespace App\Security;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
{
    private $router;
    private $session;

    public function __construct(RouterInterface $router, SessionInterface $session)
    {
        $this->router = $router;
        $this->session = $session;
    }

    // ...

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey)
    {
        // Obtenha o usuário logado
        $user = $token->getUser();

        // Liste os papeis do usuário
        $papeis = $user->getPapeis();

        // Armazene os papeis na sessão
        $this->session->set('papeis', $papeis);

        // Redirecione para uma página onde o usuário pode selecionar um papel
        return new RedirectResponse($this->router->generate('selecionar_papel'));
    }

    // ...
}

Neste exemplo, estamos injetando o serviço de sessão no construtor e usando-o para armazenar os papéis na sessão.

Espero ter ajudado e bons estudos!

Caso este post tenha lhe ajudado, por favor, marcar como solucionado ✓.

Fala, Vitor. Eu não conheço esse padrão de UX onde o usuário escolher qual o papel dele no momento do login. Não parece fazer muito sentido do ponto de vista de experiência de usuário. Se um usuário tem o papel de admin, por exemplo, ele não precisaria selecionar esse papel na hora de se logar.

Mas, uma abordagem mais simples é simplesmente salvar o role usando a sessão (com session mesmo, sem o symfony/cache) e no método getRoles da entidade de usuário só retornar o papel que está em sessão.