【问题标题】:Symfony2 : AJAX request : How to handle authentication when needed?Symfony2:AJAX 请求:需要时如何处理身份验证?
【发布时间】:2015-10-01 09:53:43
【问题描述】:

使用 Symfony2 我实现了一个 AJAX 操作来管理我的应用程序中的一些书签(添加/删除)。因此,需要对用户进行身份验证才能继续。 我有一个将用户重定向到登录页面的解决方案,但我认为使用事件来处理此重定向会更好。

实际解决方案:

用户身份验证的检查方式与 FOSUserBundle 中相同。

路由:

fbn_guide_manage_bookmark:
    path:  /bookmark/manage
    defaults: { _controller: FBNGuideBundle:Guide:managebookmark }
    options:
        expose: true
    requirements:
        _method:  POST 

控制器:

public function manageBookmarkAction(Request $request)
{
    if ($request->isXmlHttpRequest()) {

        $user = $this->getUser();

        if (!is_object($user) || !$user instanceof UserInterface) {            
            return new JsonResponse(array('status' => 'login'));
        } 

        // DO THE STUFF
    }   
}

jQuery :

$(function() {
    $('#bookmark').click(function() {
        $.ajax({
            type: 'POST',                  
            url: Routing.generate('fbn_guide_manage_bookmark'),
            data : xxxx, // SOME DATA
            success: function(data) {                
                if (data.status == 'login') {
                    var redirect = Routing.generate('fos_user_security_login');
                    window.location.replace(redirect);
                } else {
                    // DO THE STUFF       
                }
            },
        });
    }); 
});

其他解决方案? :

为了不在控制器级别验证用户是否经过身份验证,我会在安全配置文件中保护我的路由:

安全性:

security:
    access_control:
        - { path: ^/(fr|en)/bookmark/manage, role: ROLE_USER }

控制器:

public function manageBookmarkAction(Request $request)
{
    if ($request->isXmlHttpRequest()) {

        $user = $this->getUser();

        // THIS VERIFCATION SHOULD NOW BE REMOVED
        /*
        if (!is_object($user) || !$user instanceof UserInterface) {            
            return new JsonResponse(array('status' => 'login'));
        } 
        */

        // DO THE STUFF
    }   
}   

基本上,在尝试此解决方案时,Symfony2 会在内部重定向大量登录页面,正如您在 Firebug 中看到的那样:

所以我的问题是:

  1. Symfony2 在重定向之前会抛出事件或异常吗?例如,这将允许使用侦听器来捕获事件并设置 JSON 响应?
  2. 在这种情况下,应该准备什么样的响应?就像我第一个使用 HTTP 标头代码(如 302 或其他)的解决方案。如何在 AJAX 级别处理这个问题?

我可以看到一些基于异常事件的解决方案,但我认为有必要在控制器级别抛出异常,这是我想避免的。这是一个例子:

https://github.com/winzou/AssoManager/blob/master/src/Asso/AMBundle/Listener/AjaxAuthenticationListener.php

【问题讨论】:

    标签: jquery ajax symfony authentication


    【解决方案1】:

    我为 Symf4 解决了这个问题(应该与其他人没有太大不同)。异常侦听器将在重定向发生之前为 POST 提供 JSON 响应。在其他情况下,它仍会像往常一样重定向。您可以进一步自定义如何在侦听器中处理异常。

    ================================================ ========

    sevices:
    exeption_listener:
        class: Path\To\Listener\ExeptionListener
        arguments: ['@security.token_storage']
        tags:
            - { name: kernel.event_listener, event: kernel.exception }
    

    ================================================ ========

    Listener/ExeptionListener.php
    
    <?php
    
    namespace Tensor\UserBundle\Listener;
    
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
    use Symfony\Component\HttpKernel\KernelEvents;
    
    use Symfony\Component\HttpFoundation\JsonResponse;
    
    class ExeptionListener implements EventSubscriberInterface
    {
        public static function getSubscribedEvents()
        {
            // return the subscribed events, their methods and priorities
            return array(
               KernelEvents::EXCEPTION => array(
                   array('processException', 10),
                   array('logException', 0),
                   array('notifyException', -10),
               )
            );
        }
    
        public function processException(GetResponseForExceptionEvent $event)
        {
            // ...
            if (!$event->isMasterRequest()) {
                // don't do anything if it's not the master request
                return;
            }
            $request = $event->getRequest();
            if( $request->getMethod() === 'POST' ){
                $event->setResponse(new JsonResponse(array('error'=>$event->getException()->getMessage()), 403));
            }
        }
    
        public function logException(GetResponseForExceptionEvent $event)
        {
            // ...
        }
    
        public function notifyException(GetResponseForExceptionEvent $event)
        {
            // ...
        }
    }
    

    【讨论】:

      【解决方案2】:

      这里有一个解决方案(详见here):

      安全性:

      firewalls:
              main:
                  pattern:   ^/
                  anonymous: true
                  provider: fos_userbundle
                  entry_point: fbn_user.login_entry_point
                  #...
          access_control:
              - { path: ^/(fr|en)/bookmark/manage, role: ROLE_USER }
      

      服务:

      services:
      
          fbn_user.login_entry_point:
              class: FBN\UserBundle\EventListener\LoginEntryPoint
              arguments: [ @router ]
      

      服务类:

      namespace FBN\UserBundle\EventListener;
      
      use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
      use Symfony\Component\Security\Core\Exception\AuthenticationException;
      use Symfony\Component\HttpFoundation\Request;
      use Symfony\Component\HttpFoundation\JsonResponse;
      
      /**
       * When the user is not authenticated at all (i.e. when the security context has no token yet), 
       * the firewall's entry point will be called to start() the authentication process. 
       */
      
      class LoginEntryPoint implements AuthenticationEntryPointInterface
      {
          protected $router;
      
          public function __construct($router)
          {
              $this->router = $router;
          }
      
          /**
           * This method receives the current Request object and the exception by which the exception 
           * listener was triggered. 
           * 
           * The method should return a Response object
           */
          public function start(Request $request, AuthenticationException $authException = null)
          {
              if ($request->isXmlHttpRequest()) {  
      
                  return new JsonResponse('',401);
      
              }
      
              return new RedirectResponse($this->router->generate('fos_user_security_login'));
          }
      }
      

      jQuery :

      $(function() {
          $('#bookmark').click(function() {
              // DATA PROCESSING
              $.ajax({
                  type: 'POST',                  
                  url: Routing.generate('fbn_guide_manage_bookmark'),
                  data : xxxx, // SOME DATA,
                  success: function(data) {
                      // DO THE STUFF 
                  },
                  error: function(jqXHR, textStatus, errorThrown) {
                      switch (jqXHR.status) {
                          case 401:
                              var redirectUrl = Routing.generate('fos_user_security_login');
                              window.location.replace(redirectUrl);
                              break;
                          case 403: // (Invalid CSRF token for example)
                              // Reload page from server
                              window.location.reload(true);                        
                      }               
                  },
              });
          }); 
      });
      

      【讨论】:

      • 很好的答案。我一直在听kernel.controller 事件,但安全处理请求较早,我得到了登录页面 html 作为 ajax 答案。但是使用这种方法,它现在返回我可以处理的 json。
      【解决方案3】:
      1. 是的,可以按照此答案中的说明处理事件:https://stackoverflow.com/a/9182954/982075

      2. 使用 HTTP 状态码 401(未授权)或 403(禁止)

        可以使用jquery中的error函数来处理响应

        $.ajax({
            type: 'POST',                  
            url: Routing.generate('fbn_guide_manage_bookmark'),
            data : xxxx, // SOME DATA
            error: function() {
                alert("Your session has expired");
            }
        });
        

      【讨论】:

      • 听起来不错。我实际上正在实施解决方案。完成后回来接受您的建议。
      • 只有在用户没有足够的权限访问资源时才会调用拒绝访问处理程序。见here。 “当用户根本没有经过身份验证时,将调用防火墙的入口点来“启动”身份验证过程”(来自 Symfony 文档)。有关详细信息,请参阅here。以下答案中我的问题的完整代码。
      猜你喜欢
      • 2012-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-11
      • 2018-05-27
      • 1970-01-01
      相关资源
      最近更新 更多