Symfony2通过表单处理PUT跨域请求

时间:2015-12-08 17:29:47

标签: angularjs forms symfony csrf

我有两个独立的项目: UI (AngularJS)和服务器(Symfony2)。

我需要从AngularJS应用程序向Symfony2应用程序发送跨域 PUT 请求。

在Symfony控制器中,我将$request传递给form->handleRequest();并且调试显示我,使用这种方式的表单未提交。

所以,接下来我尝试将$request传递给form->submit()并收到错误“无效的CSRF令牌”。

  1. 如何通过Symfony表单正确处理跨域数据? 我已经读过将$request传递给submit()方法了 depricated。
  2. 如果我从UI发送CSRF令牌,如何将CSRF令牌传递给表单 标题? (我将csrf字段添加到请求中,但不在后端处理)
  3. 已编辑:目前我发现该问题与CSRF令牌有关。无论我如何从UI发送此令牌,它都不会在后端处理,我总是会收到“无效的CSRF令牌”错误。

    我尝试将 _token 字段直接添加到json对象,并通过csrf_field_name选项将字段名称设置为 _token 到我的formtype类中。

    我还尝试将json_decode($request->getContent(), true)传递给我的表单submit()方法,但经过调试后我看到,submitData在下一段代码中正在发生变化:

    // Symfony/Component/Form/Form.php, submit() method
    
    if ($dispatcher->hasListeners(FormEvents::PRE_SUBMIT)) {
        // here submittedData has csrf _token key/value
        $event = new FormEvent($this, $submittedData);
        $dispatcher->dispatch(FormEvents::PRE_SUBMIT, $event);
        $submittedData = $event->getData();
        // now submittedData without _token key/value
    }
    

    EDITED2 :更多详情。 CsrfValidationListener使用Symfony表单组件调用$this->tokenManager->isTokenValid(new CsrfToken($this->tokenId, $data[$this->fieldName]))并返回false,下一个代码中的问题:

    // Symfony/Component/Security/Csrf/CsrfTokenManager.php
    
    public function isTokenValid(CsrfToken $token)
        {
            if (!$this->storage->hasToken($token->getId())) {
                return false;
            }
    
            return StringUtils::equals($this->storage->getToken($token->getId()), $token->getValue());
        }
    

    似乎csrf令牌存储在会话中,因此isTokenValid()方法返回false

    我继续调试。

    EDITED3

    我可以看到,从CsrfTokenManager.php调用$this->storage->hasToken($token->getId())时会话为空。

    这很奇怪,因为我以下一种方式从我的控制器生成csrf令牌:

    $csrfToken = $this->get('security.csrf.token_manager')->refreshToken('Symfony');
    

    正如我所看到的,refreshToken()方法将csrf标记保存到db:

    // Csrf/CsrfTokenManager.php
    public function refreshToken($tokenId)
        {
            $value = $this->generator->generateToken();
    
            $this->storage->setToken($tokenId, $value);
    
            return new CsrfToken($tokenId, $value);
        }
    // Csrf/TokenStorage/SessionTokenStorage.php
    public function setToken($tokenId, $token)
        {
            if (!$this->session->isStarted()) {
                $this->session->start();
            }
    
            $this->session->set($this->namespace.'/'.$tokenId, (string) $token);
        }
    

    但是当我向表单发送数据时,从$this->tokenManager->isTokenValid(new CsrfToken($this->tokenId, $data[$this->fieldName]))的preSubmit()方法调用的CsrfValidationListener会返回空会话。

    以防我添加我的security.yml设置,也许我错过了一些东西:

    main:
        pattern: ^/(?!login).+
        stateless: true
        simple_preauth:
            authenticator: app_bundle.api_key_authenticator
        provider: api_key_user_provider
        anonymous: ~
        logout: ~
    
    login:
        pattern: ^/login
        stateless: false
        simple_preauth:
            authenticator: app_bundle.email_password_authenticator
        provider: email_user_provider
        anonymous: ~
    

    注意:我在登录防火墙下生成csrf-token并尝试从主防火墙访问它!

    但我也尝试在同一个防火墙中生成csrf-token。什么都没有改变。

    EDITED4

    我已配置自定义会话目录以跟踪会话创建。所以,我可以看到,在登录时我有所有属性的会话,但是当我做PUT请求时,我注意到创建了新的会话文件,它包含这样的内容:

    _sf2_attributes|a:0:{}_sf2_flashes|a:0:{}_sf2_meta|a:3:{s:1:"u";i:1449700968;s:1:"c";i:1449700968;s:1:"l";s:1:"0";}
    

    只是空话。

1 个答案:

答案 0 :(得分:0)

所以,我找到了csrf错误行为的原因。

创建csrf标记时,它会存储到会话中。由于我的api防火墙是无状态的,它不能存储csrf令牌。此外,每个身份验证会话都会丢弃,并且只有当前的身份验证令牌。

正确配置CORS有助于防止csrf攻击。

另见this answer about CORS headers.

希望,这对某些人有用。