背景知识:
- 我们常说的用户相关的安全操作主要就三块:认证,授权,加密。
- 本文主要介绍基于yii2的user的组件来实现用户的认证。
- 用户认证的流程:根据用户名查询数据库的记录,然后将用户输入的密码加密之后和数据库的记录进行对比,如果相等则登录成功,登录成功之后,将用户的信息存入到session中。以上就是一个简单的认证流程,当然其中你可以加入记住我等功能。由于yii2框架给我们封装好了以上的操作,因此就不需要造轮子了,接下来介绍user组件的使用。
1.在使用user组件之前我们需要先做一些准备工作:
首先需要我们的模型类实现接口 IdentityInterface,其次我们需要在web.php中配置user组件,以下给出案例:
#以下是最简单的事例,user组件管理的模型必须实现以下五个方法
class User extends ActiveRecord implements \yii\web\IdentityInterface{
//根据主键返回对象
public static function findIdentity($id)
{
return self::find()->where(['status'=>1,'id'=>$id])->one();
}
//api应用中使用较多,web应用一般不需要写函数体
public static function findIdentityByAccessToken($token, $type = null)
{
// TODO: Implement findIdentityByAccessToken() method.
}
//返回主键信息
public function getId()
{
return $this->id;
}
//以下两个方法下文会重点讲述到
public function getAuthKey()
{
return $this->auth_key;
}
public function validateAuthKey($authKey)
{
return $this->auth_key===$authKey;
}
//这个钩子函数是用来生成auth_key的,注意在数据表中建立一个auth_key字段
public function beforeSave($insert)
{
if(parent::beforeSave($insert)){
if($this->isNewRecord){
$this->auth_key=\Yii::$app->security->generateRandomString();
}
return true;
}
return false;
}
}
//web.php配置文件中加入如下组件
'user' => [
//实现IdentityInterface接口的模型类
'identityClass' => 'app\models\User',
//开启自动登录,配合记住我功能使用
'enableAutoLogin' => true,
//设置登录地址,使用场景例如在ACF授权的使用如发现用户未登录会跳转到配置的登录地址
'loginUrl'=>['login/login']
],
2.完成了第一步之后我们就可以使用user组件来管理我们的认证状态,以下是一些常见的操作:
//首先进行用户名和密码的合法性校验,之后执行如下语句,保存登录状态
\Yii::$app->user->login($user,(bool)$this->remember_me?\Yii::$app->params['session_expire_time']:0);
//$this->remember_me当用户选择记住我时,这个值会true,session_expire_time是我自己配置的记住我的有效时间
//用户退出登录时,需要销毁登录状态
\Yii::$app->user->logout()
//介绍几个常用的user组件的属性
\Yii::$app->user->identity 当前用户的身份实例,未认证的用户则为null
\Yii::$app->user->id 当前用户的id,未认证的用户则为null
\Yii::$app->user->isGuest 判断当前用户是否是游客,用户未登录返回true
3.接下来我们来讲解auth_key的用途:我在刚接触user组件的时候也对auth_key感到困惑,查阅了一些资料之后发现,auth_key只有在使用cookie登录时才会使用到。跟踪login函数你会发现一个叫做switchIdentity的函数,查看它的函数体在最后你会发现一句代码:
//如果设置了自动登录并且有效期大于0则执行sendIdentityCookie
//$duration的值就是login函数的第二个参数
if ($this->enableAutoLogin && $duration > 0) {
$this->sendIdentityCookie($identity, $duration);
}
接着我们查看sendIdentityCookie函数的实现:
protected function sendIdentityCookie($identity, $duration)
{
$cookie = Yii::createObject(array_merge($this->identityCookie, [
'class' => 'yii\web\Cookie',
'value' => json_encode([
$identity->getId(),
$identity->getAuthKey(),
$duration,
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
'expire' => time() + $duration,
]));
Yii::$app->getResponse()->getCookies()->add($cookie);
}
我们查看sendIdentityCookie的实现可以发现,当用户选择记住我时,会写入一个cookie,它的值是getId(),getAuthKey()以及有效期。因此猜测auth_key的用途应该是保证cookie更加安全吧。
4.我们找到了getAuthKey在哪里调用,那么有小伙伴肯定会问了validateAuthKey在哪里调用呢?我们可以在user组件的源代码中找到一个叫做loginByCookie的函数,其中调用了一个叫做getIdentityAndDurationFromCookie的函数,以下是这个函数的实现:
protected function getIdentityAndDurationFromCookie()
{
$value = Yii::$app->getRequest()->getCookies()->getValue($this->identityCookie['name']);
if ($value === null) {
return null;
}
$data = json_decode($value, true);
//上一步我们知道了cookie中保存了三个值,所以才有了以下的if语句
if (is_array($data) && count($data) == 3) {
list($id, $authKey, $duration) = $data;
/* @var $class IdentityInterface */
$class = $this->identityClass;
$identity = $class::findIdentity($id);
if ($identity !== null) {
if (!$identity instanceof IdentityInterface) {
throw new InvalidValueException("$class::findIdentity() must return an object implementing IdentityInterface.");
//这里它调用了validateAuthKey来验证cookie中的auth_key是否正确
} elseif (!$identity->validateAuthKey($authKey)) {
Yii::warning("Invalid auth key attempted for user '$id': $authKey", __METHOD__);
} else {
return ['identity' => $identity, 'duration' => $duration];
}
}
}
$this->removeIdentityCookie();
return null;
}
5.以上我们就简单介绍完了user组件的使用,并着重讲解了auth_key的作用(让cookie值更加安全)以及什么时候会使用到auth_key,接下来我分享一下使用user组件的经验:
查看user组件的源码你可以发现cookie的键默认是_identity,session中存的用户id默认的键是__id
public $identityCookie = ['name' => '_identity', 'httpOnly' => true];
public $idParam = '__id';
我们的web站一般都是有前后端的,那么如何前后端分离使用user组件呢?
//可以在组件中额外定义一个admin,class指定为yii\web\User即可
'admin'=>[
'class'=>'yii\web\User',
'identityClass'=>'app\modules\admin\models\Admin',
'enableAutoLogin'=>true,
'idParam'=>'__admin_id',
'identityCookie'=>['name' => '_admin_identity', 'httpOnly' => true],
'loginUrl'=>['admin/login/login']
],
'user' => [
'identityClass' => 'app\models\User',
'enableAutoLogin' => true,
'loginUrl'=>['login/login']
],
由于我们前后端都使用了user组件,为了防止前端后端的cookie和session互相覆盖,所以在admin中重新指定属性idParam和identityCookie的值。
以上就是全部啦,感谢读到最后呀。