2011-12-22 468 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