Symfony2 AJAX Войти

У меня есть пример, когда я пытаюсь создать логин AJAX, используя Symfony2 и FOSUserBundle. Я устанавливаю свои собственные success_handler и failure_handler под form_login в моем файле security.yml.

Вот класс:

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

Это отлично работает для обработки как успешных, так и неудачных попыток входа в систему AJAX. Однако, когда он включен, я не могу войти в систему с помощью метода POST стандартной формы (не AJAX). Я получаю следующую ошибку:

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

Я бы хотел, чтобы мои переопределения onAuthenticationSuccess и onAuthenticationFailure выполнялись только для XmlHttpRequests (запросов AJAX) и просто передавали выполнение исходному обработчику, если нет.

Есть ли способ сделать это?

TL; DR Я хочу, чтобы запрошенные AJAX попытки входа в систему возвращали ответ JSON для успеха и неудачи, но я хочу, чтобы это не влияло на стандартный вход в систему через форму POST.


person leek    schedule 22.12.2011    source источник
comment
У меня точно такая же проблема. Я разместил вопрос здесь: stackoverflow.com/questions/8654265/, но пока нет ответов :(   -  person David Morales    schedule 28.12.2011
comment
Здесь есть отличная статья, объясняющая, как это сделать: webtipblog.com/adding-an-ajax-login-form-to-a-symfony-project   -  person joe42    schedule 07.03.2014


Ответы (7)


ответ Дэвида хорош, но ему не хватает подробностей для новичков, так что это нужно для заполнения пробелов.

В дополнение к созданию AuthenticationHandler вам необходимо настроить его как службу, используя конфигурацию службы в пакете, в котором вы создали обработчик. Генерация пакета по умолчанию создает файл xml, но я предпочитаю yml. Вот пример файла services.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' }

Вам нужно будет изменить расширение пакета DependencyInjection, чтобы использовать yml вместо xml, например:

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

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

Затем в конфигурации безопасности вашего приложения вы устанавливаете ссылки на службу authentication_handler, которую вы только что определили:

# 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
person semateos    schedule 27.02.2012
comment
В конфигурации services.yml опечатка. Запятая (,) обязательна перед каналом. - {имя: 'monolog.logger', канал: 'безопасность' } - person Victor Henriquez; 07.06.2013
comment
Спасибо, это помогло заполнить пробелы в ответе Дэвида! - person Shawn Northrop; 20.06.2013

Если вам нужна поддержка ошибок формы FOS UserBundle, вы должны использовать:

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

вместо:

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

В первом ответе.

(конечно помните про заголовок: используйте Symfony\Component\Security\Core\SecurityContext;)

person user2048716    schedule 06.02.2013

Я полностью справился с этим с помощью 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;
        });
    }

Вот класс AjaxFormDialog. К сожалению, я еще не перенес его в плагин jQuery... https://gist.github.com/1601803< /а>

person stoefln    schedule 05.01.2012
comment
Хорошая идея, даже если она грязная (принятый ответ - лучший способ справиться с этим), она помогает. - person Laurent W.; 10.09.2014

Вы должны вернуть объект Response в обоих случаях (Ajax или нет). Добавьте «else», и все готово.

Реализация по умолчанию:

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

in AbstractAuthenticationListener::onSuccess

person DaveT    schedule 23.12.2011
comment
Проблема в том, что у меня нет доступа к экземпляру $this->httpUtils. Кроме того, я просто хочу передать выполнение обратно AbstractAuthenticationListener — я не хочу копировать и вставлять код из него в свои обработчики. - person leek; 23.12.2011
comment
Я надеялся, что вы сможете найти хороший способ сделать это, так как я точно так же, как и вы, застрял в этом ;-) - person DaveT; 25.12.2011


Это может быть не то, о чем спрашивал ОП, но я столкнулся с этим вопросом и подумал, что у других может быть та же проблема, что и у меня.

Для тех, кто реализует вход в систему AJAX, используя метод, описанный в принятом ответе, и кто ТАКЖЕ использует AngularJS для выполнения запроса AJAX, это не будет работать по умолчанию. Angular $http не устанавливает заголовки, которые Symfony использует при вызове метода $request->isXmlHttpRequest(). Чтобы использовать этот метод, вам нужно установить соответствующий заголовок в запросе Angular. Вот что я сделал, чтобы обойти проблему:

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

Прежде чем использовать этот метод, имейте в виду, что этот заголовок плохо работает с CORS. См. этот вопрос

person Sehael    schedule 29.05.2014

person    schedule
comment
@elnur ответил на этот код на аналогичный вопрос, который я задал. - person David Morales; 29.12.2011
comment
Это отличный ответ - извините, что не увидели ваш аналогичный вопрос. Тем не менее, я не собираюсь отмечать его как принятый, потому что я хочу изменить ответ только тогда, когда текущий запрос является XMLHttpRequest — в противном случае я хочу, чтобы Symfony притворялась, будто я никогда не перехватывала. - person leek; 05.01.2012
comment
Это легкая часть. Вам просто нужно вернуть json-ответ с понравившимся содержимым, а затем прочитать его через jquery. Я думаю, что самая сложная (и, к сожалению, недокументированная) часть — это обработчик входа без ajax. - person David Morales; 05.01.2012
comment
Это лучший ответ, и с его помощью я смог выполнить в основном то, что мне нужно, так что спасибо! - person leek; 13.02.2012
comment
Обратите внимание, что в этом примере отсутствует следующее, но у меня пока нет прав на редактирование :-( use Symfony\Component\Security\Core\Exception\AuthenticationException; - person MadManMonty; 20.03.2012
comment
Отредактировано! Спасибо @MadManMonty - person David Morales; 28.08.2013
comment
Спасибо, очень полезно! Я бы просто добавил, что мне нужно было перенаправить на fos_user_security_login, чтобы он заработал. - person Bonswouar; 03.03.2014
comment
Чтобы обработать вход в систему ajax/не-ajax для представления: richardmiller.co.uk/2013/02/18/ - person Laurent W.; 10.09.2014