Вход в 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.

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

В дополнение к созданию 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 
 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); } } } 

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

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

вместо:

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

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

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

Я обработал это полностью с помощью 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

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

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

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

в AbstractAuthenticationListener::onSuccess

Я сделал небольшой пакет для новых пользователей, чтобы предоставить форму входа AJAX: https://github.com/Divi/AjaxLoginBundle

Вам просто нужно заменить на form_login аутентификацию ajax_form_login в security.yml .

Не стесняйтесь предлагать новую функцию в трекере Github!

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

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

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

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