doubang9906 2015-05-12 23:47
浏览 53
已采纳

使用AJAX的Symfony 2.3和FOSUserBundle不断抛出CSRF错误

I am using Symfony 2.3 with FOSUserBundle and want to make a login form with AJAX. I found a view questions on SO (like this one) and even some other helpful sites (Adding an AJAX Login Form to a Symfony Project) that explained, how to use FOSUserBundle with ajax.

Everything works fine when I submit the form regularly, but using the ajax call I keep getting the message "Invalid CSRF token.".

Here are my configurations:

The Custom AuthenticationHandler:

namespace Moodio\Bundle\UserBundle\Handler;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;

class AuthenticationHandler implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface
{
    private $router;
    private $session;
    private $translator;
    private $csrf_provider;

    /**
     * Constructor
     */
    public function __construct( RouterInterface $router, Session $session, $translator, $csrf_provider )
    {
        $this->router  = $router;
        $this->session = $session;
        $this->translator = $translator;
        $this->csrf_provider = $csrf_provider;
    }

    /**
     * onAuthenticationSuccess
     */
    public function onAuthenticationSuccess( Request $request, TokenInterface $token )
    {
        // if AJAX login
        if ( $request->isXmlHttpRequest() ) {
            $array = array( 'success' => true); // data to return via JSON
            $response = new Response( json_encode( $array ) );
            $response->headers->set( 'Content-Type', 'application/json' );
            return $response;

        } else {// if form login

            return parent::onAuthenticationSuccess($request, $token);
        }
    }

    /**
     * onAuthenticationFailure
     *
     */
    public function onAuthenticationFailure( Request $request, AuthenticationException $exception )
    {
        // if AJAX login
        if ( $request->isXmlHttpRequest() ) {
            $result = array(
                'success' => false,
                'message' => $this->translator->trans($exception->getMessage(), array(), 'FOSUserBundle')
            ); // data to return via JSON

            $response = new Response( json_encode( $result ) );
            $response->headers->set( 'Content-Type', 'application/json' );

            return $response;


        } else {// if form login

            // set authentication exception to session
            $request->getSession()->set(SecurityContextInterface::AUTHENTICATION_ERROR, $exception);

            return new RedirectResponse( $this->router->generate( 'fos_user_security_login' ) );
        }
    }
}

src/Moodio/Bundle/MainBundle/services.yml:

services:
    moodio_main.security.authentication_handler:
        class: Moodio\Bundle\UserBundle\Handler\AuthenticationHandler
        public: false
        arguments:
            - @router
            - @session
            - @translator
            - @form.csrf_provider

security.yml firewalls:

    main:
        pattern: ^/
        form_login:
            provider: fos_userbundle
            csrf_provider: form.csrf_provider
            success_handler: moodio_main.security.authentication_handler
            failure_handler: moodio_main.security.authentication_handler
        logout:       true
        anonymous:    true

my javascript

$('#_submit').click(function(e){
    e.preventDefault();
    var frm = $('form');
    $.ajax({
        type        : frm.attr('method'),
        url         : frm.attr('action'),
        data        : frm.serialize(),
        success     : function(data, status, object) {
            if(data.error) $('.error').html(data.message);
        },
        error: function(data, status, object){
            console.log(data.message);
        }
    });
});

I tried some answers like in this answer, but it did not work for me. I actually don't understand why it should make a difference, because FOSUserBundle already creates the csrf-token and includes it in the twig template with {{ csrf_token }}.

At the moment I am using the standard FOSUserBundle templates (and only added the view lines of js I needed for the ajax).

One thing I also tried just to see if the csrf validation works, is to generate the csrf token in the onAuthenticationFailure function and check if it is valid just in the next line. In this case isCsrfTokenValid() returned true.

When I deactivate the csrf_provider in the firewall (in security.yml) I always get the message "Bad credentials" even though the credentials are right.

  • 写回答

2条回答 默认 最新

  • doushaju4901 2015-05-13 22:04
    关注

    I finally could solve my problem:

    I found the Divi-AjaxLoginBundle which works like a charm. I am not sure, what the problem was, because I had actually the same AuthenticationHandlers like this bundle, but there are also some files in the DependencyInjection folder which maybe did the job.

    Thanks to all who tried to help me.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?