我正在实现“记住我”功能(CakePHP 3),该功能将用户名和哈希密码存储在cookie中。我的问题是我无法成功将cookie中的哈希密码与数据库中的哈希密码进行匹配。
我正在使用DefaultPasswordHasher生成数据库哈希和cookie哈希。我起初以为它们会匹配,但事实并非如此。
我的第二个想法是DefaultPasswordHasher将具有一个可以验证两个散列是否来自同一密码的函数,但其check()函数采用的是纯文本密码:https://api.cakephp.org/3.3/class-Cake.Auth.DefaultPasswordHasher.html
在线上类似的问题似乎都适用于Cake的较早版本或尚无定论。
来自用户实体:
protected function _setPassword($password)
{
if (strlen($password) > 0) {
return (new DefaultPasswordHasher)->hash($password);
}
}
通过UsersController的login()函数
// Remember login?
if ($this->request->getData()['remember_me'] == "true") {
// Hash the user's password
$savedUser = [
'password' => (new DefaultPasswordHasher)->hash($this->request->getData()['password']),
'username' => $this->request->getData()['username']
];
// Save login for 1 month
$result = $this->Cookie->write('cookie_name', $savedUser, true, '1 month');
}
通过AppController的initialize()函数:
// Load logged in user
$loggedInUserID = $this->Auth->user('id');
// If not logged in try cookie
if( !$loggedInUserID && $cookie = $this->Cookie->read('cookie_name') ) {
error_log("Attempting to log in from cookie. Username: ".$cookie['username']);
$user = $this->Users->find('all', [
'conditions' => [
'username' => $cookie['username'],
'password' => $cookie['password'],
'is_deleted' => 0
]
])
->first();
// If a user was found, try to login
if ($user) {
if($this->Auth->login($user)) {
error_log("Successfully logged in from cookie. Username: ".$cookie['username']);
$loggedInUserID = $this->Auth->user('id');
} else {
error_log("Couldn't log in from cookie. Username: ".$cookie['username']);
$this->redirect('/users/logout'); // destroy session & cookie
}
}
}
(为清晰起见,我保留了我的临时错误日志消息。)
该cookie似乎已正确保存,但是其哈希与数据库不匹配,这意味着永远找不到与该cookie匹配的用户。
两个散列应该匹配的问题吗?还是我应该使用一个函数将两个散列彼此匹配?
答案 0 :(得分:0)
首先,您可能想看一下新的身份验证插件,它附带了一个cookie身份验证器!
https://book.cakephp.org/authentication/1.1/en/
话虽这么说,您的前提是错误的,两个哈希值不匹配是事实,这是设计使然,行为正确,没有方法可以匹配它们。
您应该做的是在cookie中存储一个唯一的标识符,比如说用户名,以及根据第二个凭据构建的哈希,当您尝试通过以下方式登录时需要重新构建一个哈希: Cookie数据。例如,假设用户名+哈希密码,哈希密码的哈希,因为您再次需要该确切值来重新创建纯值,以便将其与Cookie中存储的哈希进行比较,即您不能(也无论如何也不应)使用普通密码,因为在以后的请求中将不再可用。
然后,当使用cookie数据登录时,使用唯一标识符(用户名)查询用户,并通过密码哈希器的check()
方法将纯值(用户名+哈希密码)与cookie哈希进行比较
对于您给出的示例,可能看起来像这样(免责声明,这是未经测试的示例代码):
$user = $this->Users->get($this->Auth->user('id'));
$savedUser = [
'username' => $user->username,
'token' => (new DefaultPasswordHasher())->hash(
$user->username . ':' . $user->password
),
];
// ...
该查询和使用当前登录的用户,因此您必须在调用$this->Auth->identify()
之后执行此操作!
// ...
$user = $this->Users
->find()
->where([
'username' => $cookie['username'],
'is_deleted' => 0,
])
->first();
if ($user) {
$plain = $user->username . ':' . $user->password;
if ((new DefaultPasswordHasher())->check($plain, $cookie['token'])) {
// the hash matches, log the user in
$user = $user->toArray();
unset($user['password']); // but do not put the password in the session
$this->Auth->setUser($user);
} else {
// the hash doesn't match, do something else
}
}
请注意使用setUser()
,在CakePHP 3.x login()
中没有AuthComponent
方法,login()
方法来自CakePHP 2.x!同样,理想情况下,您将在自定义身份验证对象中处理所有这些操作,并在Auth.afterIdentify
事件中使用事件处理程序,并将逻辑排除在控制器之外。
另请参见