Login Symfony2 AJAX

Mam przykład, gdzie próbuję utworzyć login AJAX przy użyciu Symfony2 i FOSUserBundle. Ustawiam własne success_handler i failure_handler Pod form_login w moim pliku security.yml.

Oto Klasa:

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;
        }
    }
}

To działa świetnie do obsługi zarówno udanych, jak i nieudanych prób logowania AJAX. Jednak po włączeniu-nie jestem w stanie zalogować się za pomocą standardowej metody POST (non-AJAX). Otrzymuję następujący błąd:

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

Chciałbym, aby moje onAuthenticationSuccess i onAuthenticationFailure były tylko wykonywany dla XmlHttpRequests (żądań AJAX) i po prostu przekazuje wykonanie z powrotem do oryginalnego programu obsługi, jeśli nie.

Czy jest na to sposób?

TL; DR I want AJAX requested login attempts to return a JSON response for success and failure but I want it to not affected standard login via form POST.

Author: j0k, 2011-12-22

7 answers

Odpowiedź Davida jest dobra, ale brakuje w niej trochę szczegółów dla newbsa - więc to ma wypełnić puste pola.

Oprócz tworzenia programu AuthenticationHandler musisz skonfigurować go jako usługę, korzystając z konfiguracji usługi w pakiecie, w którym został utworzony program obsługi. Domyślne generowanie pakietu tworzy plik xml, ale wolę yml. Oto przykładowe usługi.plik 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' }

Musisz zmodyfikować rozszerzenie pakietu DependencyInjection, aby używać yml zamiast xml jak tak:

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

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

Następnie w konfiguracji zabezpieczeń aplikacji skonfigurujesz odniesienia do usługi authentication_handler, którą właśnie zdefiniowałeś:

# 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
Author: semateos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
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
Author: David Morales,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-28 09:23:23

Jeśli chcesz obsługiwać błąd formularza Fos UserBundle, musisz użyć:

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

Zamiast:

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

W pierwszej odpowiedzi.

(oczywiście pamiętaj o nagłówku: use Symfony \ Component \ Security \ Core\SecurityContext;)

 4
Author: user2048716,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-06 22:35:32

Załatwiłem to całkowicie za pomocą 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;
        });
    }

Oto Klasa AjaxFormDialog. Niestety nie przeportowałem go do wtyczki jQuery... https://gist.github.com/1601803

 3
Author: stoefln,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-23 14:14:13

Musisz zwrócić obiekt odpowiedzi w obu przypadkach(Ajax lub nie). Dodaj 'else' i możesz zaczynać.

Domyślna implementacja to:

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

W AbstractAuthenticationListener::onSuccess

 2
Author: DaveT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-23 06:05:01

Zrobiłem mały pakiet dla nowych użytkowników, aby dostarczyć formularz logowania AJAX: https://github.com/Divi/AjaxLoginBundle

Wystarczy zamienić na form_login uwierzytelnienie przez ajax_form_login w zabezpieczeniu .yml .

Zachęcamy do zaproponowania nowej funkcji w Github issue tracker !

 1
Author: Divi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-28 21:00:38

To może nie jest to, o co prosiło OP, ale natknąłem się na to pytanie i pomyślałem, że inni mogą mieć ten sam problem, co ja.

Dla tych, którzy implementują logowanie AJAX przy użyciu metody opisanej w zaakceptowanej odpowiedzi i którzy również używają AngularJS do wykonania żądania AJAX, to nie będzie działać domyślnie. $http Angular nie ustawia nagłówków używanych przez Symfony podczas wywoływania metody $request->isXmlHttpRequest(). Aby skorzystać z tej metody, należy ustawić odpowiednie nagłówek w żądaniu kątowym. To jest to, co zrobiłem, aby obejść problem:

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

Zanim użyjesz tej metody, pamiętaj, że nagłówek nie działa dobrze z CORS. Zobacz to pytanie

 0
Author: Sehael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:23