2011-12-22 98 views
40

我有一个例子,我试图创建一个使用Symfony2和FOSUserBundle的AJAX登录。我在我的security.yml文件中设置了我自己的success_handlerfailure_handlerform_loginSymfony2 AJAX登录

这里是类:

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

我想为我的onAuthenticationSuccessonAuthenticationFailure覆盖到只对XmlHttpRequests执行(AJAX请求)和简单的手工执行回原来的处理程序,如果没有。

有没有办法做到这一点?

TL; DR我希望AJAX请求的登录尝试返回成功和失败的JSON响应,但我希望它不会影响通过表单POST的标准登录。

+1

我有同样的确切问题。我在这里发布了一个问题:http://stackoverflow.com/questions/8654265/calling-default-handler-from-a-custom-authentication-handler但现在没有答案:( – 2011-12-28 10:32:07

+1

有一个伟大的文章解释如何做这在这里:http://www.webtipblog.com/adding-an-ajax-login-form-to-a-symfony-project/ – joe42 2014-03-07 12:51:36

回答

48

David的answer很好,但它缺少一些关于newbs的细节 - 所以这是为了填补空白。

除了创建AuthenticationHandler之外,还需要使用创建处理程序的包中的服务配置将其设置为服务。默认的bundle生成一个xml文件,但我更喜欢yml。以下是一个示例服务。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束扩展使用阳明而不是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 
+0

services.yml配置中存在拼写错误,通道前需要使用逗号(,) 。 - {name:'monolog.logger',channel:'security'} – 2013-06-07 08:06:30

+0

谢谢,这确实有助于填补Davids答案的空白! – 2013-06-20 19:30:05

2

你必须在两种情况下(Ajax或不)返回一个Response对象。添加一个“其他”,你很好。

默认的实现是:

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

AbstractAuthenticationListener::onSuccess

+0

这个问题是,我没有任何访问' $ this-> httpUtils'。另外,我只想将执行回传给'AbstractAuthenticationListener' - 我不想将代码复制并粘贴到我的处理程序中。 – leek 2011-12-23 17:49:08

+0

我希望你能找到一个很好的方法来做到这一点因为我完全像你这样卡住;-) – DaveT 2011-12-25 03:49:09

30
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); 
     } 
    } 
} 
+0

这段代码是由@elnur回答的,我提出了类似的问题。 – 2011-12-29 15:26:37

+0

这是一个很好的答案 - 道歉没有看到类似的问题。但是,我不打算将其标记为已接受,因为我只想在当前请求是XMLHttpRequest时修改响应 - 否则,我希望Symfony假装我从未截获过。 – leek 2012-01-05 09:41:46

+1

这是最简单的部分。你只需要返回一个你喜欢的内容的json响应,然后通过jquery读取它。我认为最困难的(也是令人遗憾的未解决的)部分是非ajax登录处理程序。 – 2012-01-05 15:54:17

3

我用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

+0

好主意,即使它很脏(接受的答案是处理这个问题的最佳方法),它会有帮助 – 2014-09-10 12:14:08

4

如果你想在FOS UserBundle形状误差的支持,你必须使用:

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

代替:

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

在第一个答案。

(当然记得头:使用的Symfony \分量\安全\核心\ SecurityContext的;)

1

我为新用户提供一个AJAX登录表单的小包袱:https://github.com/Divi/AjaxLoginBundle

你只必须替换为form_login通过身份验证ajax_form_loginsecurity.yml

随意在Github问题跟踪器中提供新功能!

0

这可能不是OP所要求的,但我遇到了这个问题,并认为其他人可能会遇到与我一样的问题。

对于那些正在使用接受的答案中描述的方法实现AJAX登录的人,以及谁也使用AngularJS来执行AJAX请求,默认情况下这不起作用。在调用$request->isXmlHttpRequest()方法时,Angular的$http未设置Symfony正在使用的标头。为了使用此方法,您需要在Angular请求中设置适当的标题。这是我为解决问题所做的:

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

在使用此方法之前,请注意,此标头不适用于CORS。见this question