密码
密码
绝大多数开发者都知道,密码不能以纯文本的形式存储,但仍有许多开发者认为,使用 md5
或 sha1
对密码进行哈希处理是安全的。曾几何时,使用上述的哈希算法就足够了,但是,现代硬件使用暴力攻击使得快速破解此类哈希和更强的哈希成为可能。
为了提高用户密码的安全性,即使在最坏的情况下(例如,当您的应用程序被攻破),您也需要使用能够抵御暴力破解攻击的哈希算法。目前最好的选择是 bcrypt
。在 PHP 中,你可以使用 crypt 函数 创建一个 bcrypt
哈希。Yii 提供了两个辅助函数,让使用 crypt
更容易安全地生成和验证散列。
生成密码
当用户首次提供密码时(例如,注册时),密码需要被哈希处理,Yii 的 yii\base\Security
安全组件,提供了 generatePasswordHash()
方法,来根据一个密码和随机盐,最终生成一个安全的哈希,且他可以存储在数据库中。代码如下:
// 生成哈希(通常在用户注册或修改密码时完成)
$hash = Yii::$app->security->generatePasswordHash($password);
// ...将 $hash 存入数据库 ...
第二个参数 $cost
,是用于 Blowfish 哈希算法 的 Cost 参数,Cost 值越高,生成哈希并据此验证密码所需的时间就越长。较高的 Cost ,可以减缓对密码的暴力攻击。为了最好地防止暴力破解攻击,请将其设置为生产服务器上可容忍的最高值。$cost 的值每增加 1,计算哈希的时间就会翻一倍。
$hash = Yii::$app->security->generatePasswordHash($password);
yii\base\Security::passwordHashStrategy
属性,当被设置为 ‘crypt’,输出总是 60 位的 ASCII 字符;当被设置为 ‘password_hash’,在未来的 PHP 版本中,输出的长度可能会增加,参考 password_hash()。从 Yii 2.0.7 版本开始,generatePasswordHash()
忽略 passwordHashStrategy
,当它可用时,使用 password_hash()
,或不可用的时候,使用 crypt()
。
然后,该散列可以与相应的模型属性相关联,这样就可以将其存储在数据库中以供以后使用。可以将这个方法写在 User 模型中,例如:
namespace common\models;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
class User extends ActiveRecord implements IdentityInterface
{
/**
* Generates password hash from password and sets it to the model * * @param string $password
*/
public function setPassword($password)
{
$this->password_hash = Yii::$app->security->generatePasswordHash($password);
}
}
验证密码
在注册稍后,当用户试图登录时,提交的密码必须与之前已经哈希化,且存储在数据库的密码进行验证,可以将它们传递给 validatePassword()
方法。代码如下:
// 在登录期间,使用从数据库获取的 $hash 验证输入的密码是否正确
if (Yii::$app->security->validatePassword($password, $hash)) {
// 密码 OK
} else {
// 密码错误
}
你可以将验证密码的方法写在 User 模型中,类似这样,具体可以参考高级应用模板的示例代码:
namespace common\models;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
class User extends ActiveRecord implements IdentityInterface
{
/**
* Validates password
*
* @param string $password password to validate
* @return bool if password provided is valid for current user
*/
public function validatePassword($password)
{
return Yii::$app->security->validatePassword($password, $this->password_hash);
}
}
重置密码
当用户要进行密码重置申请的时候,系统会将重置密码的 token 字段一起存储数据库 user 表中,并发送一份邮件给用户,邮件中的链接带有密码重置的 token,当用户点击链接,会进行验证重置密码的 token,验证通过即可,进行重置密码操作,Yii 提供了一个生成 密码重置 token 的方法,具体如下:
$password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
具体控制器示例代码如下:
namespace frontend\controllers;
use Yii;
use yii\web\Controller;
use frontend\models\PasswordResetRequestForm;
use frontend\models\ResetPasswordForm;
class SiteController extends Controller
{
/**
* Requests password reset.
*
* @return mixed
*/
public function actionRequestPasswordReset()
{
$model = new PasswordResetRequestForm();
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
if ($model->sendEmail()) {
Yii::$app->session->setFlash('success', 'Check your email for further instructions.');
return $this->goHome();
}
Yii::$app->session->setFlash('error', 'Sorry, we are unable to reset password for the provided email address.');
}
return $this->render('requestPasswordResetToken', [
'model' => $model,
]);
}
/**
* Resets password.
*
* @param string $token
* @return mixed
* @throws BadRequestHttpException
*/
public function actionResetPassword($token)
{
try {
$model = new ResetPasswordForm($token);
} catch (InvalidArgumentException $e) {
throw new BadRequestHttpException($e->getMessage());
}
if ($model->load(Yii::$app->request->post()) && $model->validate() && $model->resetPassword()) {
Yii::$app->session->setFlash('success', 'New password saved.');
return $this->goHome();
}
return $this->render('resetPassword', [
'model' => $model,
]);
}
}
User 模型中的代码:
namespace common\models;
use yii\db\ActiveRecord;
use yii\web\IdentityInterface;
class User extends ActiveRecord implements IdentityInterface
{
/**
* Generates new password reset token
*/
public function generatePasswordResetToken()
{
$this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
}
/**
* Finds out if password reset token is valid * * @param string $token password reset token
* @return bool
*/
public static function isPasswordResetTokenValid($token)
{
if (empty($token)) {
return false;
}
$timestamp = (int) substr($token, strrpos($token, '_') + 1);
$expire = Yii::$app->params['user.passwordResetTokenExpire'];
return $timestamp + $expire >= time();
}
/**
* Removes password reset token
*/
public function removePasswordResetToken()
{
$this->password_reset_token = null;
}
}
💖喜欢本文档的,欢迎点赞、收藏、留言或转发,谢谢支持!
作者邮箱:zhuzixian520@126.com,github地址:github.com/zhuzixian520