Symfony2:AJAX请求:如何在需要时处理身份验证?

时间:2015-07-13 08:02:56

标签: jquery ajax symfony authentication

使用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中看到的那样:

enter image description here

所以我的问题是:

  1. Symfony2在重定向之前是抛出事件还是异常?这将允许使用侦听器来捕获事件并设置JSON响应,例如?
  2. 在这种情况下,应该准备什么样的回应?像我的第一个使用HTTP标头代码(如302)(或其他东西)的东西解决方案。如何在AJAX级别处理此问题?
  3. 我可以看到一些异常事件解决方案,但我认为有必要在控制器级别抛出异常,这是我想避免的。这是一个例子:

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

3 个答案:

答案 0 :(得分:10)

这是一个解决方案(详见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);                        
                }               
            },
        });
    }); 
});

答案 1 :(得分:1)

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

答案 2 :(得分:0)

我为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)
    {
        // ...
    }
}