Symfony2 AJAX Login

j'ai un exemple où j'essaie de créer un login AJAX en utilisant Symfony2 et FOSUserBundle. Je mets mes propres success_handler et failure_handler sous form_login dans mon fichier security.yml .

Voici la classe:

class AjaxAuthenticationListener implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface
{  
    /**
     * This is called when an interactive authentication attempt succeeds. This
     * is called by authentication listeners inheriting from
     * AbstractAuthenticationListener.
     *
     * @see SymfonyComponentSecurityHttpFirewallAbstractAuthenticationListener
     * @param Request        $request
     * @param TokenInterface $token
     * @return Response the response to return
     */
    public function onAuthenticationSuccess(Request $request, TokenInterface $token)
    {
        if ($request->isXmlHttpRequest()) {
            $result = array('success' => true);
            $response = new Response(json_encode($result));
            $response->headers->set('Content-Type', 'application/json');
            return $response;
        }
    }

    /**
     * This is called when an interactive authentication attempt fails. This is
     * called by authentication listeners inheriting from
     * AbstractAuthenticationListener.
     *
     * @param Request                 $request
     * @param AuthenticationException $exception    
     * @return Response the response to return
     */
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        if ($request->isXmlHttpRequest()) {
            $result = array('success' => false, 'message' => $exception->getMessage());
            $response = new Response(json_encode($result));
            $response->headers->set('Content-Type', 'application/json');
            return $response;
        }
    }
}

cela fonctionne très bien pour gérer les tentatives de connexion AJAX réussies et ratées. Cependant, lorsque activé - Je ne peux pas me connecter via la méthode standard form POST (non-AJAX). Je reçois l'erreur suivante:

Catchable Fatal Error: Argument 1 passed to SymfonyComponentHttpKernelEventGetResponseEvent::setResponse() must be an instance of SymfonyComponentHttpFoundationResponse, null given

j'aimerais que mes onAuthenticationSuccess et onAuthenticationFailure ne soient exécutés que pour les requêtes XMLHTTPREQUESTS (requêtes AJAX) et qu'ils remettent simplement l'exécution au gestionnaire d'origine si ce n'est pas le cas.

y a-t-il un moyen de le faire?

TL; DR je veux QU'AJAX demandé tentatives de connexion pour retourner une réponse JSON pour le succès et l'échec, mais je veux qu'il n'affecte pas la connexion standard via form POST.

42
demandé sur j0k 2011-12-22 20:53:16

7 réponses

David réponse est bonne, mais il manque un peu de détail pour les newbs - c'est donc à remplir les blancs.

en plus de créer le Gestionnaire D'authentification, vous devez le configurer comme un service en utilisant la configuration du service dans le paquet où vous avez créé le gestionnaire. La génération de bundle par défaut crée un fichier xml, mais je préfère yml. Voici un exemple de services.fichier yml:

#src/Vendor/BundleName/Resources/config/services.yml

parameters:
    vendor_security.authentication_handler: Vendor\BundleName\Handler\AuthenticationHandler

services:
    authentication_handler:
        class:  %vendor_security.authentication_handler%
        arguments:  [@router]
        tags:
            - { name: 'monolog.logger', channel: 'security' }

vous auriez besoin de modifier L'extension DependencyInjection bundle pour utiliser yml au lieu de xml comme suit:

#src/Vendor/BundleName/DependencyInjection/BundleExtension.php

$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');

ensuite, dans la configuration de sécurité de votre application, vous définissez les références au service authentication_handler que vous venez de définir:

# app/config/security.yml

security:
    firewalls:
        secured_area:
            pattern:    ^/
            anonymous: ~
            form_login:
                login_path:  /login
                check_path:  /login_check
                success_handler: authentication_handler
                failure_handler: authentication_handler
49
répondu semateos 2017-05-23 12:02:23
namespace YourVendor\UserBundle\Handler;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;

class AuthenticationHandler
implements AuthenticationSuccessHandlerInterface,
           AuthenticationFailureHandlerInterface
{
    private $router;

    public function __construct(Router $router)
    {
        $this->router = $router;
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token)
    {
        if ($request->isXmlHttpRequest()) {
            // Handle XHR here
        } else {
            // If the user tried to access a protected resource and was forces to login
            // redirect him back to that resource
            if ($targetPath = $request->getSession()->get('_security.target_path')) {
                $url = $targetPath;
            } else {
                // Otherwise, redirect him to wherever you want
                $url = $this->router->generate('user_view', array(
                    'nickname' => $token->getUser()->getNickname()
                ));
            }

            return new RedirectResponse($url);
        }
    }

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        if ($request->isXmlHttpRequest()) {
            // Handle XHR here
        } else {
            // Create a flash message with the authentication error message
            $request->getSession()->setFlash('error', $exception->getMessage());
            $url = $this->router->generate('user_login');

            return new RedirectResponse($url);
        }
    }
}
30
répondu David Morales 2013-08-28 09:23:23

si vous voulez le support d'erreur FOS UserBundle form, vous devez utiliser:

$request->getSession()->set(SecurityContext::AUTHENTICATION_ERROR, $exception);

au lieu de:

$request->getSession()->setFlash('error', $exception->getMessage());

Dans la première réponse.

(rappelez-vous bien sûr de l'en-tête: utilisez Symfony\Component\Security\Core\SecurityContext;)

4
répondu user2048716 2013-02-06 22:35:32

j'ai géré cela entièrement avec javascript:

if($('a.login').length > 0) { // if login button shows up (only if logged out)
        var formDialog = new MyAppLib.AjaxFormDialog({ // create a new ajax dialog, which loads the loginpage
            title: 'Login',
            url: $('a.login').attr('href'),
            formId: '#login-form',
            successCallback: function(nullvalue, dialog) { // when the ajax request is finished, look for a login error. if no error shows up -> reload the current page
                if(dialog.find('.error').length == 0) {
                    $('.ui-dialog-content').slideUp();
                    window.location.reload();
                }
            }
        });

        $('a.login').click(function(){
            formDialog.show();
            return false;
        });
    }

Voici la classe AjaxFormDialog. Malheureusement, je ne l'ai pas encore porté sur un plugin jQuery... https://gist.github.com/1601803

3
répondu stoefln 2012-02-23 14:14:13

vous devez retourner un objet de réponse dans les deux cas (Ajax ou non). Ajoute un "autre" et tu es prêt à partir.

l'implémentation par défaut est:

$response = $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request));

dans AbstractAuthenticationListener::onSuccess

2
répondu DaveT 2011-12-23 06:05:01

j'ai fait un petit paquet pour les nouveaux utilisateurs de fournir un formulaire de connexion AJAX : https://github.com/Divi/AjaxLoginBundle

il suffit de remplacer form_login authentification par ajax_form_login dans la sécurité .yml .

n'hésitez pas à proposer une nouvelle fonctionnalité dans le GitHub issue tracker !

1
répondu Divi 2013-02-28 21:00:38

ce n'est peut-être pas ce que l'OP a demandé, mais je suis tombé sur cette question, et j'ai pensé que d'autres pourraient avoir le même problème que moi.

pour ceux qui implémentent un login AJAX en utilisant la méthode décrite dans la réponse acceptée et qui utilisent aussi AngularJS pour effectuer la requête AJAX, cela ne fonctionnera pas par défaut. Le $http d'Angular ne définit pas les en-têtes que Symfony utilise lorsqu'il appelle la méthode $request->isXmlHttpRequest() . Afin d'utiliser cette méthode, vous devez définir l'en-tête approprié dans la requête angulaire. C'est ce que j'ai fait pour contourner le problème:

$http({
    method  : 'POST',
    url     : {{ path('login_check') }},
    data    : data,
    headers: {'X-Requested-With': 'XMLHttpRequest'}
})

avant d'utiliser cette méthode, soyez conscient que cet en-tête ne fonctionne pas bien avec CORS. Voir cette question

0
répondu Sehael 2017-05-23 12:02:23