当我尝试登录时,请求被Security
组件阻塞。我怎样才能让它正常工作?
我有一个简单的登录表单
<div class="container container-login">
<h2><?php echo __('Login'); ?></h2>
<div class="wrap-form-signin">
<?php
echo $this->Form->create('User', array('action' => 'login', 'class' => 'form-signin'));
echo $this->Form->input('username', array('label' => '', 'placeholder' => __('Email')));
echo $this->Form->input('password', array('label' => '', 'placeholder' => __('Password')));
echo $this->Form->submit(__('Login'));
echo $this->Form->end();
?>
</div>
</div>
控制器的动作如下:
public function login() {
if ($this->request->is('post')) {
if ($this->Auth->login()) {
return $this->redirect($this->Auth->redirectUrl());
} else {
$this->Session->setFlash(__('Username or password is incorrect'), 'default', array(), 'auth');
}
}
}
Security
组分包含在AppController
public $components = array('Security', ... );
在error.log中我得到:
2013-03-29 13:40:58 Error: [BadRequestException] The request has been black-holed
Request URL: /users/login
Stack Trace:
#0 C:wampwwwcdxlibCakeControllerComponentSecurityComponent.php(234): SecurityComponent->blackHole(Object(UsersController), 'auth')
#1 [internal function]: SecurityComponent->startup(Object(UsersController))
#2 C:wampwwwcdxlibCakeUtilityObjectCollection.php(131): call_user_func_array(Array, Array)
#3 [internal function]: ObjectCollection->trigger(Object(CakeEvent))
#4 C:wampwwwcdxlibCakeEventCakeEventManager.php(247): call_user_func(Array, Object(CakeEvent))
#5 C:wampwwwcdxlibCakeControllerController.php(670): CakeEventManager->dispatch(Object(CakeEvent))
#6 C:wampwwwcdxlibCakeRoutingDispatcher.php(183): Controller->startupProcess()
#7 C:wampwwwcdxlibCakeRoutingDispatcher.php(161): Dispatcher->_invoke(Object(UsersController), Object(CakeRequest), Object(CakeResponse))
#8 C:wampwwwcdxappwebrootindex.php(92): Dispatcher->dispatch(Object(CakeRequest), Object(CakeResponse))
#9 {main}
我怎样才能找到是什么使我的请求进入黑洞?
当我尝试使用自定义黑洞处理程序时,错误类型是auth
。但这是我能得到的所有信息
我的CakePHP版本是2.3.1
编辑:在没有Security
组件的情况下,登录可以正常工作。添加到AppController
后,无法正常登录
EDIT2:我没有任何data[_Token][key]
字段的形式
EDIT3解决方案:我的团队中有人重写了HTMLHelper类,它遗漏了_tags数组中的"hiddenblock",这导致丢失了_Token字段。有关详细信息,请参阅我和thaJeztah的回答以及下面的评论
您以什么顺序添加组件?安全组件应该放在其他在startup()
:
"如果你正在使用安全组件的表单保护功能和其他在startup()回调中处理表单数据的组件,请确保在$components数组中将安全组件置于这些组件之前。"
安全因为AuthComponent 处理startup()
内部的表单数据,我认为这是适用的,所以要确保SecurityComponent 在你的$components数组 AuthComponent之前;
public $components = array(
'Security',
'Session',
'Auth' => array(
// auth component settings
)
);
更新OP发布的"最终"答案清楚地表明这个问题不可能被回答。结果是,团队中有人修改了HtmlHelper,导致不输出'hidden'块,因此不输出CSRF令牌。
在正常情况下,你不应该修改CakePHP框架文件本身。CakePHP提供了一些方法来覆盖它的功能(包括helper),而不需要修改CakePHP的"核心"文件。
为什么修改CakePHP文件不好
以汽车为例。如果某机械师不喜欢设计并决定"交换"刹车和加速踏板怎么办?
当然,如果你知道这个修改,汽车仍然能够驾驶。然而,如果没有这些重要的信息,任何其他驱动程序肯定会崩溃(并且想知道刚才发生了什么!?)
如果框架的默认行为不适合你的需要,扩展那些类。不要修改框架文件本身(除非真的没有其他选择)。如果对框架的修改是绝对必要的,请务必与团队讨论,并就所做的更改编写文档。
请记住,如果不将相同的修改应用到更新的版本,则无法将框架更新到新版本。同样,如果更改没有记录,有人可能会更新CakePHP并破坏您的修改。
另外,如果您覆盖或修改CakePHP,请确保覆盖与CakePHP的默认行为兼容,并且CakePHP的单元测试仍然正确运行(或为修改创建新的单元测试)
在CakePHP中使用'自定义'帮助器
如果您需要自定义CakePHP helper(或其他组件),有各种选项可以这样做,而无需修改CakePHP文件;
1。扩展Helper
class AwesomeHtmlHelper extends HtmlHelper {
/**
* enhanced tableHeaders method, outputs tableHeaders in a 'thead' tag
*
* {@inheritdoc}
*/
public function tableHeaders(array $names, array $trOptions = null, array $thOptions = null)
{
$output = parent::tableHeaders($names, $trOptions, $thOptions);
return '<thead>' . $output . '</thead>';
}
}
然后,按常规方式使用Helper:
echo $this->AwesomeHtml->tableHeaders(array('Date', 'Title', 'Active'));
2。"插入式"替换-使用别名(CakePHP> 2.3)
从CakePHP 2.3开始,可以为Helper使用别名。此功能可用于(例如)在您的应用程序中存在两个具有相同名称的helper(例如Plugin.HtmlHelper)的情况。
同样,这允许您用自己的Helper重写CakePHP Helper。查看这里的文档:使用和配置helper
请注意,这将在应用程序中
的所有地方重写Helper !public $helpers = array(
'Html' => array(
'className' => 'AwesomeHtml'
)
);
现在,$this->Html
实际上指的是视图中的AwesomeHtmlHelper
:
echo $this->eHtml->tableHeaders(array('Date', 'Title', 'Active'));
将输出您的'增强'表头
编辑:
return $this->Html->useTag('hiddenblock', $out);
行,这是一个团队成员对HtmlHelper
的修改,我不知道,阻止了_Token
输入在页面上打印。主要问题仍然存在,那就是表单中缺少data[_Token][key]
输入,我必须添加
我终于发现问题是什么了。由于
FormHelper
类中可能存在错误,因此没有_Token
字段。我必须编辑secure
方法,看起来像这样:
public function secure($fields = array()) {
if (!isset($this->request['_Token']) ||
empty($this->request['_Token'])) {
return;
}
$locked = array();
$unlockedFields = $this->_unlockedFields;
foreach ($fields as $key => $value) {
if (!is_int($key)) {
$locked[$key] = $value;
unset($fields[$key]);
}
}
sort($unlockedFields, SORT_STRING);
sort($fields, SORT_STRING);
ksort($locked, SORT_STRING);
$fields += $locked;
$locked = implode(array_keys($locked), '|');
$unlocked = implode($unlockedFields, '|');
$fields = Security::hash(serialize($fields) . $unlocked . Configure::read('Security.salt'), 'sha1');
$out = $this->hidden('_Token.fields', array(
'value' => urlencode($fields . ':' . $locked),
'id' => 'TokenFields' . mt_rand()
));
$out .= $this->hidden('_Token.unlocked', array(
'value' => urlencode($unlocked),
'id' => 'TokenUnlocked' . mt_rand()
));
return $this->Html->useTag('hiddenblock', $out);
}
这个方法返回一个空字符串。因此,不返回useTag
的结果,而是返回$out
。
我必须做的另一件事是添加data[_Token][key]
字段。最后,该方法看起来像这样:
public function secure($fields = array()) {
if (!isset($this->request['_Token']) ||
empty($this->request['_Token'])) {
return;
}
$locked = array();
$unlockedFields = $this->_unlockedFields;
foreach ($fields as $key => $value) {
if (!is_int($key)) {
$locked[$key] = $value;
unset($fields[$key]);
}
}
sort($unlockedFields, SORT_STRING);
sort($fields, SORT_STRING);
ksort($locked, SORT_STRING);
$fields += $locked;
$locked = implode(array_keys($locked), '|');
$unlocked = implode($unlockedFields, '|');
$fields = Security::hash(serialize($fields) . $unlocked . Configure::read('Security.salt'), 'sha1');
$key = $this->request['_Token']['key'];
$out = $this->hidden('_Token.fields', array(
'value' => urlencode($fields . ':' . $locked),
'id' => 'TokenFields' . mt_rand()
));
$out .= $this->hidden('_Token.key', array(
'value' => $key,
'id' => 'TokenKey' . mt_rand()
));
$out .= $this->hidden('_Token.unlocked', array(
'value' => urlencode($unlocked),
'id' => 'TokenUnlocked' . mt_rand()
));
return $out;
//return $this->Html->useTag('hiddenblock', $out);
}
这是很难测试与您提供的信息,但我会尝试改变形式创建调用:echo $this->Form->create('User', array('url' => array('controller' => 'users', 'action' => 'login'), 'class' => 'form-signin'));
auth
类型的错误指示表单验证错误,或控制器/动作不匹配错误。如果显示表单的页面不是/users/login
,则可能发生这种情况,因为使用安全组件时跨控制器通信是黑洞。希望能有所帮助
编辑:验证错误也可能指向您没有为Auth组件定义username
的事实,检查cookbook
回答,因为我无法评论。您是否尝试过向Security组件添加回调,看看它究竟生成了什么?
看:http://book.cakephp.org/2.0/en/core-libraries/components/security-component.html#handling-blackhole-callbacks
您打算使用Security组件做什么?你想要阻止什么?
您是否尝试使用会话令牌而不是每个表单令牌?http://book.cakephp.org/2.0/en/core-libraries/components/security-component.html csrf保护
public $components = array(
'Security' => array(
'csrfUseOnce' => false
)
);
我有同样的问题与用户登录表单从登录。并在另一个视图中使用它作为元素,并通过使用$this-> form ->end()关闭表单来解决这个问题,否则它将不会添加来自令牌....的隐藏输入