18 votos

Symfony2 AJAX Login

Tengo un ejemplo en el que estoy tratando de crear un AJAX login usando Symfony2 y FOSUserBundle. Yo soy mi propio success_handler y failure_handler bajo form_login mi security.yml archivo.

Aquí está la clase:

class AjaxAuthenticationListener implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface
{  
    /**
     * This is called when an interactive authentication attempt succeeds. This
     * is called by authentication listeners inheriting from
     * AbstractAuthenticationListener.
     *
     * @see \Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener
     * @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;
        }
    }
}

Esto funciona muy bien para el manejo correcto y error de AJAX intentos de inicio de sesión. Sin embargo, cuando se activa - soy incapaz de inicio de sesión a través de la forma estándar método POST (no-AJAX). Recibo el siguiente error:

Catchable Fatal Error: Argument 1 passed to Symfony\Component\HttpKernel\Event\GetResponseEvent::setResponse() must be an instance of Symfony\Component\HttpFoundation\Response, null given

Me gustaría para mi onAuthenticationSuccess y onAuthenticationFailure reemplaza a ejecutarse sólo para XmlHttpRequests (peticiones AJAX) y simplemente la ejecución de vuelta al controlador original si no.

Es allí una manera de hacer esto?

TL;DR quiero AJAX de inicio de sesión solicitado intenta devolver un JSON de respuesta para el éxito y el fracaso, pero yo quiero que no afectan estándar de inicio de sesión a través de la forma de POST.

31voto

semateos Puntos 443

David la respuesta es buena, pero falta un pequeño detalle para newbs - así que esto es para llenar los espacios en blanco.

Además de la creación de la AuthenticationHandler tendrás que configurarlo como un servicio mediante el servicio de configuración en el paquete donde se creó el controlador. El paquete predeterminado generación crea un archivo xml, pero yo prefiero yml. He aquí un ejemplo de los servicios.fichero yaml:

#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' }

Se debería modificar el DependencyInjection paquete de extensión para uso yml en lugar de xml como el siguiente:

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

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

A continuación, en la aplicación de configuración de seguridad configurar las referencias a la authentication_handler servicio que acabamos de definir:

# 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

24voto

David Puntos 5988
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);
        }
    }
}

4voto

user2048716 Puntos 41

Si desea que el FOS UserBundle error de formulario de soporte, usted debe utilizar:

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

en lugar de:

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

En la primera respuesta.

(por supuesto que debes recordar sobre el encabezado: use Symfony\Component\Security\Core\SecurityContext;)

3voto

stoefln Puntos 2717

He manejado enteramente con 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;
        });
    }

Aquí está el AjaxFormDialog clase. Por desgracia no he portado a un plugin de jQuery por ahora... https://gist.github.com/1601803

2voto

DaveT Puntos 21

Usted debe devolver un objeto de Respuesta en ambos casos (Ajax o no). Añadir una `persona' y ya está bueno para ir.

La implementación por defecto es:

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

en AbstractAuthenticationListener::onSuccess

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by: