【问题标题】:Symfony2 processing PUT cross-domain request via formsSymfony2 通过表单处理 PUT 跨域请求
【发布时间】:2015-12-13 07:05:07
【问题描述】:

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

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

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

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

  1. 如何通过 Symfony 表单正确处理跨域数据? 我读过将$request 传递给 submit() 方法是 已贬值。
  2. 如果我通过 UI 从 UI 发送 CSRF 令牌,如何将其传递给表单 标题? (我在请求中添加了 csrf 字段,但它不在后端处理)

已编辑:目前我看到该问题与 CSRF 令牌有关。无论我如何从 UI 发送此令牌,它都不会在后端进行处理,并且我总是收到“Invalid CSRF token”错误。

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

我也尝试将json_decode($request->getContent(), true) 传递给我的表单submit() 方法,但在调试后我看到,submittedData 在下一个代码中发生了变化:

// 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 token是存入session的,所以isTokenValid()方法返回false

我继续调试。

EDITED3

如我所见,从 CsrfTokenManager.php 调用 $this->storage->hasToken($token->getId()) 时会话为空。

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

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

如我所见,refreshToken() 方法将 csrf 令牌保存到数据库中:

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

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

以防我添加我的 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";}

只是空会话。

【问题讨论】:

  • 您要禁用 csrf 保护吗?
  • @Matteo 不,我认为最好离开 csrf .. 我想将它与 CORS 一起使用

标签: angularjs forms symfony csrf


【解决方案1】:

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

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

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

另见this answer about CORS headers.

希望,这对某人有帮助。

【讨论】:

    猜你喜欢
    • 2017-04-29
    • 1970-01-01
    • 2011-12-23
    • 1970-01-01
    • 2015-07-10
    • 2013-09-06
    • 2014-08-26
    • 1970-01-01
    • 2013-03-26
    相关资源
    最近更新 更多