Yii2 在一个浏览器中修改用户密码后,另一个浏览器(存有cookie)依然可以直接登录
问题描述
Yii2 在一个浏览器中修改用户密码后,另一个浏览器(存有cookie)依然可以直接登录。
如果想要修改用户密码后其他浏览器也需要重新登录,改如何实现?
配置文件:
'user' => [
'class' => 'common\components\User',
'identityClass' => 'common\models\User',
# 自动登录这个配置不想改。
'enableAutoLogin' => true,
'loginUrl' => ['site/login'],
'identityCookie' => [
'name' => '_identity',
'httpOnly' => true,
'domain' => APP_COOKIE_DOMAIN,
],
],
'session' => [
'class' => 'yii\web\Session',
'cookieParams' => [
'path' => '/',
'domain' => APP_COOKIE_DOMAIN,
'httpOnly' => true,
],
],
先交代以下背景,否则这个需求会显得很奇怪。
管理员身份可以直接修改所有用户的密码,而普通用户则没有权限修改密码。
这个需求的目的是管理员修改某用户密码后该用户不能再使用该账号登录,而目前的情况是,只要浏览器中有该站点的cookie,用户可以不验证密码直接登录到该站点。
通过阅读 YII2 源码,发现 'enableAutoLogin' => true
自动登录机制是通过 cookie 进行自动登录的。
cookie实例:
_csrf=2696417ff2b74c12c655acfab8455e2c916423206cb57e6ad34cb25a17da41cfa:2:{i:0;s:5:"_csrf";i:1;s:32:"W3HFB5P-OtcdpUb8I7Jq2t_OQ4CXMnRK";};
PHPSESSID:=dbe5bn6f700oaj8qoipmtmfsm4;
_identity=1fcae021294423d23da60c0ca46fc0c336b3684c6792f11bdf8fec5e0b620479a:2:{i:0;s:9:"_identity";i:1;s:49:["32","BjUNqsR5wqVgOIDAUgrgEGaN2LvRr6VB",2592000]";}
可见,Cookie主要包含以下三部分:
_csrf:防止跨站请求伪造。
PHPSESSID:session ID, 可在服务器的session存储目录中找到指定文件名的session文件。
_identity:自动登录最重要的部分。包含身份信息。
其中 a:2:{i:0;s:9:"_identity";i:1;s:49:["32","BjUNqsR5wqVgOIDAUgrgEGaN2LvRr6VB",2592000]";}
是数组的序列化字符串,可反序列化为一个数组。
[
"_identity" => [
"32", # 用户ID
"BjUNqsR5wqVgOIDAUgrgEGaN2LvRr6VB", # auth_key user表中的字段,类似密码
2592000 # 有效期 :此处为 30 天
]
]
\yii\web\User
/**
* Logs in a user by cookie.
*
* This method attempts to log in a user using the ID and authKey information
* provided by the [[identityCookie|identity cookie]].
*/
protected function loginByCookie()
{
$value = Yii::$app->getRequest()->getCookies()->getValue($this->identityCookie['name']);
if ($value === null) {
return;
}
$data = json_decode($value, true);
if (count($data) !== 3 || !isset($data[0], $data[1], $data[2])) {
return;
}
list ($id, $authKey, $duration) = $data;
/* @var $class IdentityInterface */
$class = $this->identityClass;
$identity = $class::findIdentity($id);
if ($identity === null) {
return;
} elseif (!$identity instanceof IdentityInterface) {
throw new InvalidValueException("$class::findIdentity() must return an object implementing IdentityInterface.");
}
if ($identity->validateAuthKey($authKey)) {
if ($this->beforeLogin($identity, true, $duration)) {
$this->switchIdentity($identity, $this->autoRenewCookie ? $duration : 0);
$ip = Yii::$app->getRequest()->getUserIP();
Yii::info("User '$id' logged in from $ip via cookie.", __METHOD__);
$this->afterLogin($identity, true, $duration);
}
} else {
Yii::warning("Invalid auth key attempted for user '$id': $authKey", __METHOD__);
}
}
由 $identity->validateAuthKey($authKey)
可看出,开启自动登录时验证的是 authKey 。所以只需要在修改用户密码时同时修改 authKey 即可使用户再次输入网站地址进入站点时不自动登录,而是跳转到登录页面重新登录。
参考代码:
common\models\User.php
public function rules()
{/*{{{*/
return [
// other
['password', 'hashPassword'],
];
}/*}}}*/
public function hashPassword($attr, $options)
{/*{{{*/
$this->auth_key = Yii::$app->getSecurity()->generateRandomString();
$this->password = Yii::$app->getSecurity()->generatePasswordHash($this->$attr);
}/*}}}*/
这种方式在IE,Firefox中都非常实用。
然而在chrome中却很不理想,因为IE,Firefox在关闭浏览器时服务器中对应的session就会失效,再次打开站点时就会重新建立session。而chrome却很另类(可能对用户来说这是个很不错的功能),关闭chrome浏览器很久之后再打开时,session依然是有效的,这样就既跳过了密码验证,有跳过了 authKey 验证。
于是我决定又追加设置了 session 的有效期:
config\main.php
'session' => [
'class' => 'yii\web\Session',
'cookieParams' => [
'path' => '/',
'domain' => APP_COOKIE_DOMAIN,
'httpOnly' => true,
'lifetime' => 3600,
],
],
添加这个设置后,若修改了用户密码,用户超过一小时未活跃,再次登录该站点时会跳转到登录页面,属于用户名和密码重新登录。
而未修改密码的用户,若超过一小时未活跃,则依然可以通过cookie的 authKey 验证,直接进入该站点。
这篇关于Yii2 在一个浏览器中修改用户密码后,另一个浏览器(存有cookie)依然可以直接登录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!