为什么 AJAX 请求会触发 400 响应,尽管使用安全组件解锁了操作?
Why do AJAX requests trigger a 400 response despite unlocking the actions with the security component?
我正在尝试让 ajax 请求在 CakePHP4 中工作,但将 运行 保留在 CSRF 保护中。
在我的控制器中:
public function beforeFilter(EventInterface $event)
{
parent::beforeFilter($event);
$this->Security->setConfig('unlockedActions', ['players']);
}
public function players()
{
$players = ['Sjaak Afhaak', 'Aad Kippezaad', 'Gras Kaffer', 'Tedje van Es'];
if ($this->request->is('ajax')) {
$this->response = $this->response->withDisabledCache();
}
$letter = trim($this->request->getData('letter'));
if (!empty($letter)) {
$players = array_filter($spelers, function ($haystack) use ($letter) {
return(strpos($haystack, $letter));
});
}
$this->set(compact('players'));
$this->viewBuilder()->setOption('serialize', ['players']);
$this->RequestHandler->renderAs($this, 'json');
}
然后在模板文件中:
<?php echo $this->Html->script('jquery.min'); ?>
<div class="container">
<div class="row">
<div class="column">
<div class="users form content">
<?php echo $this->Form->create(null, ['url' => \Cake\Routing\Router::pathUrl('Ado::players')]); ?>
<fieldset>
<legend>Players</legend>
<?php echo $this->Form->text('letter', ['placeholder' => 'Begin letter(s)']); ?>
</fieldset>
<?= $this->Form->button('OK'); ?>
<?= $this->Form->end() ?>
</div>
</div>
<div class="column response">
...
</div>
</div>
</div>
<script>
$(document).ready(function () {
$(document).on("submit", "form", function (event) {
var $form = $(this);
var $target = $('div.response');
var csrf = $('input[name=_csrfToken]', $form).val();
var data = { letter: $('input[name=letter]', $form).val() };
$target.html('');
$.ajax({
method: "POST",
url: $form.attr('action'),
beforeSend: function(xhr) {
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('X-CSRF-Token', csrf);
},
data: data,
dataType: "json"
})
.done(function (response) {
var items = [];
$.each( response, function( key, val ) {
items.push( "<li id='" + key + "'>" + val + "</li>" );
});
$( "<ul/>", {
"class": "spelers-list",
html: items.join( "" )
}).appendTo( $target );
});
event.preventDefault();
});
});
</script>
如果 ajax 调用中没有 beforeSend
,我会收到 403 响应。
如果我包含 X-CSRF-Token,我会收到 400 响应。
2020-06-18 09:49:38 Error: [Cake\Http\Exception\BadRequestException] `_Token` was not found in request data. in src\vendor\cakephp\cakephp\src\Controller\Component\FormProtectionComponent.php on line 143
Stack Trace:
- src\vendor\cakephp\cakephp\src\Controller\Component\FormProtectionComponent.php:97
- src\vendor\cakephp\cakephp\src\Event\EventManager.php:309
- src\vendor\cakephp\cakephp\src\Event\EventManager.php:286
- src\vendor\cakephp\cakephp\src\Event\EventDispatcherTrait.php:92
- src\vendor\cakephp\cakephp\src\Controller\Controller.php:569
- src\vendor\cakephp\cakephp\src\Controller\ControllerFactory.php:72
- src\vendor\cakephp\cakephp\src\Http\BaseApplication.php:229
- src\vendor\cakephp\cakephp\src\Http\Runner.php:77
- src\vendor\cakephp\cakephp\src\Http\Middleware\BodyParserMiddleware.php:164
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\authorization\src\Middleware\AuthorizationMiddleware.php:129
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\authentication\src\Middleware\AuthenticationMiddleware.php:124
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\cakephp\src\Http\Runner.php:77
- src\vendor\cakephp\cakephp\src\Http\Middleware\CsrfProtectionMiddleware.php:138
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\cakephp\src\Http\Runner.php:58
- src\vendor\cakephp\cakephp\src\Routing\Middleware\RoutingMiddleware.php:166
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\cakephp\src\Routing\Middleware\AssetMiddleware.php:68
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\cakephp\src\Error\Middleware\ErrorHandlerMiddleware.php:119
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\debug_kit\src\Middleware\DebugKitMiddleware.php:60
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\cakephp\src\Http\Runner.php:58
- src\vendor\cakephp\cakephp\src\Http\Server.php:90
- src\webroot\index.php:41
不确定是否相关,但我正在使用身份验证插件。 (https://book.cakephp.org/authentication/2/en/index.html)
从堆栈跟踪中可以看出,错误源于 form protection component,而不是安全组件,因此对安全组件的解锁操作不会执行任何操作。
安全组件已弃用(Cookbook 似乎没有提及),表单保护组件是要替换它的实用程序之一(其他是 CSRF middleware and the HTTPS enforcer middleware)- 你不应该同时使用两者,删除安全组件(我的意思是也删除相关的 loadComponent()
调用),并相应地配置表单保护组件!
$this->FormProtection->setConfig('unlockedActions', ['players']);
这里的文档确实需要大修,不仅安全组件没有弃用通知,而且表单保护组件也没有列在组件部分。
我正在尝试让 ajax 请求在 CakePHP4 中工作,但将 运行 保留在 CSRF 保护中。
在我的控制器中:
public function beforeFilter(EventInterface $event)
{
parent::beforeFilter($event);
$this->Security->setConfig('unlockedActions', ['players']);
}
public function players()
{
$players = ['Sjaak Afhaak', 'Aad Kippezaad', 'Gras Kaffer', 'Tedje van Es'];
if ($this->request->is('ajax')) {
$this->response = $this->response->withDisabledCache();
}
$letter = trim($this->request->getData('letter'));
if (!empty($letter)) {
$players = array_filter($spelers, function ($haystack) use ($letter) {
return(strpos($haystack, $letter));
});
}
$this->set(compact('players'));
$this->viewBuilder()->setOption('serialize', ['players']);
$this->RequestHandler->renderAs($this, 'json');
}
然后在模板文件中:
<?php echo $this->Html->script('jquery.min'); ?>
<div class="container">
<div class="row">
<div class="column">
<div class="users form content">
<?php echo $this->Form->create(null, ['url' => \Cake\Routing\Router::pathUrl('Ado::players')]); ?>
<fieldset>
<legend>Players</legend>
<?php echo $this->Form->text('letter', ['placeholder' => 'Begin letter(s)']); ?>
</fieldset>
<?= $this->Form->button('OK'); ?>
<?= $this->Form->end() ?>
</div>
</div>
<div class="column response">
...
</div>
</div>
</div>
<script>
$(document).ready(function () {
$(document).on("submit", "form", function (event) {
var $form = $(this);
var $target = $('div.response');
var csrf = $('input[name=_csrfToken]', $form).val();
var data = { letter: $('input[name=letter]', $form).val() };
$target.html('');
$.ajax({
method: "POST",
url: $form.attr('action'),
beforeSend: function(xhr) {
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('X-CSRF-Token', csrf);
},
data: data,
dataType: "json"
})
.done(function (response) {
var items = [];
$.each( response, function( key, val ) {
items.push( "<li id='" + key + "'>" + val + "</li>" );
});
$( "<ul/>", {
"class": "spelers-list",
html: items.join( "" )
}).appendTo( $target );
});
event.preventDefault();
});
});
</script>
如果 ajax 调用中没有 beforeSend
,我会收到 403 响应。
如果我包含 X-CSRF-Token,我会收到 400 响应。
2020-06-18 09:49:38 Error: [Cake\Http\Exception\BadRequestException] `_Token` was not found in request data. in src\vendor\cakephp\cakephp\src\Controller\Component\FormProtectionComponent.php on line 143
Stack Trace:
- src\vendor\cakephp\cakephp\src\Controller\Component\FormProtectionComponent.php:97
- src\vendor\cakephp\cakephp\src\Event\EventManager.php:309
- src\vendor\cakephp\cakephp\src\Event\EventManager.php:286
- src\vendor\cakephp\cakephp\src\Event\EventDispatcherTrait.php:92
- src\vendor\cakephp\cakephp\src\Controller\Controller.php:569
- src\vendor\cakephp\cakephp\src\Controller\ControllerFactory.php:72
- src\vendor\cakephp\cakephp\src\Http\BaseApplication.php:229
- src\vendor\cakephp\cakephp\src\Http\Runner.php:77
- src\vendor\cakephp\cakephp\src\Http\Middleware\BodyParserMiddleware.php:164
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\authorization\src\Middleware\AuthorizationMiddleware.php:129
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\authentication\src\Middleware\AuthenticationMiddleware.php:124
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\cakephp\src\Http\Runner.php:77
- src\vendor\cakephp\cakephp\src\Http\Middleware\CsrfProtectionMiddleware.php:138
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\cakephp\src\Http\Runner.php:58
- src\vendor\cakephp\cakephp\src\Routing\Middleware\RoutingMiddleware.php:166
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\cakephp\src\Routing\Middleware\AssetMiddleware.php:68
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\cakephp\src\Error\Middleware\ErrorHandlerMiddleware.php:119
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\debug_kit\src\Middleware\DebugKitMiddleware.php:60
- src\vendor\cakephp\cakephp\src\Http\Runner.php:73
- src\vendor\cakephp\cakephp\src\Http\Runner.php:58
- src\vendor\cakephp\cakephp\src\Http\Server.php:90
- src\webroot\index.php:41
不确定是否相关,但我正在使用身份验证插件。 (https://book.cakephp.org/authentication/2/en/index.html)
从堆栈跟踪中可以看出,错误源于 form protection component,而不是安全组件,因此对安全组件的解锁操作不会执行任何操作。
安全组件已弃用(Cookbook 似乎没有提及),表单保护组件是要替换它的实用程序之一(其他是 CSRF middleware and the HTTPS enforcer middleware)- 你不应该同时使用两者,删除安全组件(我的意思是也删除相关的 loadComponent()
调用),并相应地配置表单保护组件!
$this->FormProtection->setConfig('unlockedActions', ['players']);
这里的文档确实需要大修,不仅安全组件没有弃用通知,而且表单保护组件也没有列在组件部分。