正文
当我们用yii2开始项目时,在migrate初始生成user表时,会看到里面有一个password_reset_token字段,这个字段是做什么用的呢?查阅好多资料后,还是云里雾里,莫名其妙,不知所从。其实这个字段是在忘记密码情况下用来重设密码的。
登录页面,忘记密码了,点击reset it:
跳转到:
输入你的邮箱,然后去邮箱点击链接地址,重设密码就可以了。
我们看看输入邮箱的页面代码:
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();
} else {
Yii::$app->session->setFlash('error', 'Sorry, we are unable to reset password for the provided email address.');
}
}
return $this->render('requestPasswordResetToken', [
'model' => $model,
]);
}
我们看看PasswordResetRequestForm类的sendEmail()做了什么:
public function sendEmail()
{
$user = User::findOne([
'status' => User::STATUS_ACTIVE,
'email' => $this->email,
]);
if (!$user) {
return false;
}
if (!User::isPasswordResetTokenValid($user->password_reset_token)) {
$user->generatePasswordResetToken(); // 生成唯一password_reset_token
if (!$user->save()) {
return false;
}
}
return Yii::$app
->mailer
->compose(
['html' => 'passwordResetToken-html', 'text' => 'passwordResetToken-text'],
['user' => $user]
)
->setFrom([Yii::$app->params['supportEmail'] => Yii::$app->name . ' robot'])
->setTo($this->email)
->setSubject('Password reset for ' . Yii::$app->name)
->send();
}
这里就是生成唯一password_reset_token:
public function generatePasswordResetToken()
{
$this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time();
}
并存入用户表,然后组装url发送给用户,url如下:
http://yii2_1108_frontend.host/index.php?r=site/reset-password&token=Asgm1LhqV4QXyg2sFyKrUjkNIx7jxugQ_1510203686
这里的token就是用户表的password_reset_token 。
我们看一下邮件:
当我们点击这个链接时,跳转到设置密码页:
我们看一下这一页的代码:
public function actionResetPassword($token)
{
try {
$model = new ResetPasswordForm($token);
} catch (InvalidParamException $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,
]);
}
我们看一下ResetPasswordForm类的构造方法:
public function __construct($token, $config = [])
{
if (empty($token) || !is_string($token)) {
throw new InvalidParamException('Password reset token cannot be blank.');
}
$this->_user = User::findByPasswordResetToken($token);
if (!$this->_user) {
throw new InvalidParamException('Wrong password reset token.');
}
parent::__construct($config);
}
以及resetPassword()方法:
public function resetPassword()
{
$user = $this->_user;
$user->setPassword($this->password);
$user->removePasswordResetToken();
return $user->save(false);
}
我们再看一下model类user的removePasswordResetToken()清除user用户表中的password_reset_token:
public function removePasswordResetToken()
{
$this->password_reset_token = null;
}
这样这个重设密码的地址就只能用一次了,也没有留下漏洞。
然后你就可以用新密码登录了。
另外有一处,model类user的isPasswordResetTokenValid()方法:
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();
}
这里会校验操作用户的password_reset_token,在找回密码时长内则不能再发送邮件;如果超出找回密码时长则不能重设密码。