【问题标题】:Symfony 2.3 and FOSUserBundle with AJAX keeps throwing CSRF-Errors带有 AJAX 的 Symfony 2.3 和 FOSUserBundle 不断抛出 CSRF 错误
【发布时间】:2015-07-24 01:26:52
【问题描述】:

我正在使用带有 FOSUserBundle 的 Symfony 2.3,并希望使用 AJAX 制作登录表单。我发现了一个关于 SO(如 this one)甚至其他一些有用的网站(Adding an AJAX Login Form to a Symfony Project)的视图问题,其中解释了如何将 FOSUserBundle 与 ajax 一起使用。

当我定期提交表单时一切正常,但使用 ajax 调用我不断收到消息“无效的 CSRF 令牌。”。

这是我的配置:

自定义身份验证处理程序:

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 防火墙:

    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

我的 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);
        }
    });
});

我尝试了一些类似this answer 的答案,但它对我不起作用。我实际上不明白为什么它应该有所作为,因为 FOSUserBundle 已经创建了 csrf-token 并将其包含在带有 {{ csrf_token }} 的 twig 模板中。

目前我正在使用标准的 FOSUserBundle 模板(并且只添加了 ajax 所需的 js 视图行)。

我还尝试查看 csrf 验证是否有效,即在 onAuthenticationFailure 函数中生成 csrf 令牌并在下一行检查它是否有效。在这种情况下 isCsrfTokenValid() 返回 true。

当我停用防火墙中的 csrf_provider(在 security.yml 中)时,即使凭据正确,我总是会收到“错误凭据”消息。

【问题讨论】:

  • 我不明白为什么要使用AJAX实现登录。因为在登录 Symfony2 或任何自定义应用程序时都会启动 SESSION,会写入一些 COOKIES 来识别已建立的连接。并且对于那个浏览器将需要刷新。登录后使用AJAX,但不使用AJAX登录用户。除非您正在为此开发 Web 服务,否则您可以在不启动会话的情况下使用基于令牌的身份验证,因为像本机移动应用程序这样的所有客户端都不会轻易启动 SESSIONS。

标签: php jquery ajax symfony fosuserbundle


【解决方案1】:

如果您使用的是最新版本的 FOSUserBundle,则 csrf 提供程序已更改为 security.csrf.token_manager。您的表单可能正在使用 form.csrf_provider 生成令牌,而 FOSUB 可能正在使用另一个令牌。要么:

  1. 将您的 FOSUserBundle 依赖项显式设置为 1.2 或 1.3,而不是 dev-master
  2. 手动将 csrf 提供程序设置为正确的服务。

无论哪种方式,清除您的 cookie 并重新启动您的开发服务器以确保其中没有旧信息。

【讨论】:

    【解决方案2】:

    我终于可以解决我的问题了:

    我发现Divi-AjaxLoginBundle 就像一个魅力。我不确定问题出在哪里,因为我实际上有与此捆绑包相同的 AuthenticationHandlers,但 DependencyInjection 文件夹中也有一些文件可能完成了这项工作。

    感谢所有试图帮助我的人。

    【讨论】:

    • 好的,我明白了,这是一个非常愚蠢的错误。我们使用的 jquery.js 有一些奇怪的变化(可能是同事做的),影响了 ajax 请求。使用官方 jquery 版本一切正常。
    猜你喜欢
    • 2014-01-19
    • 2013-06-15
    • 2012-08-16
    • 1970-01-01
    • 2016-06-02
    • 1970-01-01
    • 2015-08-29
    • 2016-06-01
    • 1970-01-01
    相关资源
    最近更新 更多