the symfony2 node must fosuser fos_user fos db_driver configured child php symfony fosuserbundle

php - node - security symfony2



Symfony2 AJAX Login (7)

Tengo un ejemplo en el que intento crear un inicio de sesión AJAX usando Symfony2 y FOSUserBundle. Estoy configurando mi propio success_handler y failure_handler en form_login en mi archivo security.yml .

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 manejar intentos exitosos y fallidos de inicio de sesión de AJAX. Sin embargo, cuando está habilitado, no puedo iniciar sesión a través del método POST estándar (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 que mis onAuthenticationSuccess y onAuthenticationFailure solo se ejecuten para XmlHttpRequests (solicitudes AJAX) y que simplemente devuelvan la ejecución al controlador original si no es así.

¿Hay alguna forma de hacer esto?

TL; DR Deseo que AJAX solicite los intentos de inicio de sesión para devolver una respuesta JSON para el éxito y el fracaso, pero quiero que no afecte el inicio de sesión estándar a través del formulario POST.


Debe devolver un objeto Response en ambos casos (Ajax o no). Agrega un `else ''y listo.

La implementación predeterminada es:

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

en AbstractAuthenticationListener::onSuccess


Hice un pequeño paquete para nuevos usuarios para proporcionar un formulario de inicio de sesión de AJAX: https://github.com/Divi/AjaxLoginBundle

Solo tiene que reemplazar la autenticación form_login por ajax_form_login en security.yml .

¡No dudes en sugerir nuevas funciones en el rastreador de problemas de Github!


La answer de David es buena, pero le falta un pequeño detalle para los recién nacidos, así que esto es para llenar los espacios en blanco.

Además de crear AuthenticationHandler, deberá configurarlo como un servicio utilizando la configuración del servicio en el paquete donde creó el controlador. La generación de paquetes predeterminada crea un archivo xml, pero prefiero yml. Aquí hay un ejemplo de archivo 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'' }

Tendría que modificar la extensión del paquete DependencyInjection para usar yml en lugar de xml de la siguiente manera:

#src/Vendor/BundleName/DependencyInjection/BundleExtension.php $loader = new Loader/YamlFileLoader($container, new FileLocator(__DIR__.''/../Resources/config'')); $loader->load(''services.yml'');

Luego, en la configuración de seguridad de su aplicación, configura las referencias al servicio authentication_handler que acaba 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


Manejé esto completamente 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á la clase AjaxFormDialog. Lamentablemente, no lo he portado a un plugin de jQuery ahora ... https://gist.github.com/1601803


Puede que esto no sea lo que pidió el OP, pero me encontré con esta pregunta y pensé que otros podrían tener el mismo problema que yo.

Para aquellos que están implementando un inicio de sesión AJAX usando el método que se describe en la respuesta aceptada y que TAMBIÉN están usando AngularJS para realizar la solicitud AJAX, esto no funcionará de forma predeterminada. El $http Angular no configura los encabezados que usa Symfony cuando llama al método $request->isXmlHttpRequest() . Para utilizar este método, debe establecer el encabezado apropiado en la solicitud angular. Esto es lo que hice para evitar el problema:

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

Antes de utilizar este método, tenga en cuenta que este encabezado no funciona bien con CORS. Ver esta pregunta


Si desea el soporte de error de formulario FOS UserBundle, debe usar:

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

en lugar de:

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

En la primera respuesta.

(por supuesto, recuerde sobre el encabezado: use Symfony / Component / Security / Core / SecurityContext;)


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