Несоответствие токена CSRF в тестах cakephp3.7
Тесты /TestCase/Controller/FeedbackControllerTest.php:45
public function testAdd()
{
$this->enableCsrfToken();
$this->enableSecurityToken();
$this->session([
'Auth' => [
'User' => [
'id' => 1,
'role' => 'REPR',
]
]
]);
$this->configRequest([
'headers' => ['Accept' => 'application/json']
]);
$_data = [
'crash' => 1,
'details' => 'Lorem ipsum dolor sit amet'
];
$_data = json_encode($_data, JSON_PRETTY_PRINT);
$this->post('/feedback/add', $_data); // <---- 45
$expected = [
'status' => 'success'
];
$expected = json_encode($expected, JSON_PRETTY_PRINT);
$this->assertEquals($expected, (string)$this->_response->getBody());
}
Выход PHPUnit:
1) App\Test\TestCase\Controller\FeedbackControllerTest::testAdd
Cake\Http\Exception\InvalidCsrfTokenException: Missing CSRF token cookie
/vagrant/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:196
/vagrant/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:120
/vagrant/vendor/cakephp/cakephp/src/Http/Middleware/CsrfProtectionMiddleware.php:106
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:65
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:51
/vagrant/vendor/cakephp/cakephp/src/Routing/Middleware/RoutingMiddleware.php:168
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:65
/vagrant/vendor/cakephp/cakephp/src/Routing/Middleware/AssetMiddleware.php:88
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:65
/vagrant/vendor/cakephp/cakephp/src/Error/Middleware/ErrorHandlerMiddleware.php:96
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:65
/vagrant/vendor/cakephp/cakephp/src/Http/Runner.php:51
/vagrant/vendor/cakephp/cakephp/src/Http/Server.php:98
/vagrant/vendor/cakephp/cakephp/src/TestSuite/MiddlewareDispatcher.php:201
/vagrant/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestTrait.php:516
/vagrant/vendor/cakephp/cakephp/src/TestSuite/IntegrationTestTrait.php:413
/vagrant/tests/TestCase/Controller/FeedbackControllerTest.php:45
Я прочитал и попробую решения из ответов:
Как создать токен CSRF для тестирования PHPunit Cakephp 3?
если я добавлю, как @ndm говорит:
$token = 'my-csrf-token';
$this->cookie('csrfToken', $token);
$data = [
'email' => 'info@example.com',
'password' => 'secret',
'_csrfToken' => $token
];
Затем:
Cake \ Http \ Exception \ InvalidCsrfTokenException: несоответствие токена CSRF.
Как исправить?
2 ответа
При передаче строки в виде данных POST тестовый пример интеграции не будет автоматически устанавливать токены, ни токен CSRF, ни токен безопасности, поскольку он не может ничего вставить в строку, не зная о формате данных. Следовательно, он также не будет устанавливать cookie.
Таким образом, в случаях, когда вы передаете строковые данные, вы должны установить cookie и токен вручную, как описано в ответе, который вы связали. Однако при использовании чего-либо, кроме application/x-www-form-urlencoded
данные (т.е. данные, которые PHP будет декодировать и помещать в $_POST
superglobal), в вашем примере данных JSON вы должны передать токен в качестве заголовка, потому что входные данные JSON будут декодированы компонентом обработчика запросов (есть планы переместить их на уровень промежуточного программного обеспечения IIRC), который выполняется после Промежуточное программное обеспечение CSRF, которое, следовательно, не будет видеть никаких пост-данных.
Пример:
$token = 'my-csrf-token';
$this->cookie('csrfToken', $token);
$this->configRequest([
'headers' => [
'X-CSRF-Token' => $token,
// ...
]
]);
Токены компонента безопасности, с другой стороны, должны были бы войти в данные POST, компонент безопасности не будет искать заголовки, и у него будет доступ к декодированным данным после запуска компонента обработчика запроса (убедитесь, что вы загружаете запрос Компонент обработчика перед компонентом безопасности!). Вы можете обратиться к \Cake\TestSuite\IntegrationTestTrait::_addTokens()
Чтобы понять, как создаются токены безопасности, вы должны сделать это примерно так:
$url = '/feedback/add';
$_data = [
'crash' => 1,
'details' => 'Lorem ipsum dolor sit amet'
];
$keys = array_map(
function ($field) {
return preg_replace('/(\.\d+)+$/', '', $field);
},
array_keys(Hash::flatten($_data))
);
$tokenData = $this->_buildFieldToken($url, array_unique($keys));
$_data['_Token'] = $tokenData;
$_data['_Token']['debug'] = 'SecurityComponent debug data would be added here';
Обратите внимание, что URL, который передается _buildFieldToken()
также должен был бы включать возможные данные строки запроса!
Можете ли вы попробовать этот код в вашей функции jquery ajax,
beforeSend: function (xhr)
{
xhr.setRequestHeader('X-CSRF-Token', $('[name="_csrfToken"]').val());
},
поставить код перед функцией успеха