Yii的用戶認證分爲兩個部分,一個是User組件,負責管理用戶認證狀態的,包括登陸,登出,檢測當前登陸狀態等,源文件位於vender/yiisoft/yii2/web/User.php。另外一個是實現接口IdentityInterface的模型,同時必須繼承ActiveRecord,當用戶登陸註冊時,組件User會經過模型中的接口方法,對用戶進行驗證。php
對於用戶狀態切換主要經過switchIdentity方法實現的,好比註冊後,用戶登陸時,會用到User組件中的switchIdentity方法,以下:ios
/** * Switches to a new identity for the current user. * * When [[enableSession]] is true, this method may use session and/or cookie to store the user identity information, * according to the value of `$duration`. Please refer to [[login()]] for more details. * * This method is mainly called by [[login()]], [[logout()]] and [[loginByCookie()]] * when the current user needs to be associated with the corresponding identity information. * * @param IdentityInterface|null $identity the identity information to be associated with the current user. * If null, it means switching the current user to be a guest. * @param integer $duration number of seconds that the user can remain in logged-in status. * This parameter is used only when `$identity` is not null. */ public function switchIdentity($identity, $duration = 0) { $this->setIdentity($identity); if (!$this->enableSession) { return; } /* Ensure any existing identity cookies are removed. */ if ($this->enableAutoLogin) { $this->removeIdentityCookie(); } $session = Yii::$app->getSession(); if (!YII_ENV_TEST) { $session->regenerateID(true); } $session->remove($this->idParam); $session->remove($this->authTimeoutParam); if ($identity) { $session->set($this->idParam, $identity->getId()); if ($this->authTimeout !== null) { $session->set($this->authTimeoutParam, time() + $this->authTimeout); } if ($this->absoluteAuthTimeout !== null) { $session->set($this->absoluteAuthTimeoutParam, time() + $this->absoluteAuthTimeout); } if ($duration > 0 && $this->enableAutoLogin) { $this->sendIdentityCookie($identity, $duration); } } }
若是寫入identity爲null,則將用戶狀態設置爲離線,若是不是null,而是模型實例,該方法會將用戶模型的id值寫入session中,用戶打開新頁面時,只需下面的方法就能夠判斷是否已經登陸。web
//已登陸返回true Yii::$app->user->isGuest
訪問user組件的isGuest屬性,會經過魔術方法,調用User組件中的getIsGuest方法bootstrap
/** * Returns a value indicating whether the user is a guest (not authenticated). * @return boolean whether the current user is a guest. * @see getIdentity() */ public function getIsGuest() { return $this->getIdentity() === null; }
方法又調用getIdentity()方法yii2
/** * Returns the identity object associated with the currently logged-in user. * When [[enableSession]] is true, this method may attempt to read the user's authentication data * stored in session and reconstruct the corresponding identity object, if it has not done so before. * @param boolean $autoRenew whether to automatically renew authentication status if it has not been done so before. * This is only useful when [[enableSession]] is true. * @return IdentityInterface|null the identity object associated with the currently logged-in user. * `null` is returned if the user is not logged in (not authenticated). * @see login() * @see logout() */ public function getIdentity($autoRenew = true) { if ($this->_identity === false) { if ($this->enableSession && $autoRenew) { $this->_identity = null; $this->renewAuthStatus(); } else { return null; } } return $this->_identity; }
當session啓用時,經過renewAuthStatus()更新新用戶狀態cookie
/** * Updates the authentication status using the information from session and cookie. * * This method will try to determine the user identity using the [[idParam]] session variable. * * If [[authTimeout]] is set, this method will refresh the timer. * * If the user identity cannot be determined by session, this method will try to [[loginByCookie()|login by cookie]] * if [[enableAutoLogin]] is true. */ protected function renewAuthStatus() { $session = Yii::$app->getSession(); $id = $session->getHasSessionId() || $session->getIsActive() ? $session->get($this->idParam) : null; if ($id === null) { $identity = null; } else { /* @var $class IdentityInterface */ $class = $this->identityClass; $identity = $class::findIdentity($id); } $this->setIdentity($identity); if ($identity !== null && ($this->authTimeout !== null || $this->absoluteAuthTimeout !== null)) { $expire = $this->authTimeout !== null ? $session->get($this->authTimeoutParam) : null; $expireAbsolute = $this->absoluteAuthTimeout !== null ? $session->get($this->absoluteAuthTimeoutParam) : null; if ($expire !== null && $expire < time() || $expireAbsolute !== null && $expireAbsolute < time()) { $this->logout(false); } elseif ($this->authTimeout !== null) { $session->set($this->authTimeoutParam, time() + $this->authTimeout); } } if ($this->enableAutoLogin) { if ($this->getIsGuest()) { $this->loginByCookie(); } elseif ($this->autoRenewCookie) { $this->renewIdentityCookie(); } } }
該方法主要經過session取出用戶id,而後經過id獲取用戶模型實例,而後使用實例進行登陸,和更新認證過時時間。session
下面實現一個經常使用的用戶註冊登陸功能模塊,用戶只有登陸後才能夠進入home頁面app
User模型:dom
<?php /** * Created by PhpStorm. * User: zhenbao * Date: 16/10/17 * Time: 下午4:14 */ namespace app\models; use Yii; use yii\web\IdentityInterface; use yii\db\ActiveRecord; class User extends ActiveRecord implements IdentityInterface { const LOGIN = "login"; const REGISTER = "register"; public function scenarios() { return [ self::LOGIN => ['username', 'password'], self::REGISTER => ['username', 'password'] ]; } public static function tableName() { return "user"; } public static function findIdentity($id) { return static::findOne($id); } public static function findIdentityByAccessToken($token, $type = null) { return static::findOne(['accessToken' => $token]); } public function getId() { return $this -> id; } public function getAuthKey() { return $this -> authKey; } public function validateAuthKey($authKey) { return $this -> getAuthKey() === $authKey; } public static function findIdentityByUsername($username) { return static::findOne(['username' => $username]); } public function validatePassword($password) { return $this -> password === sha1($password); } public function setPassword() { $this -> password = sha1($this -> password); return true; } public function beforeSave($insert) { if(parent::beforeSave($insert)) { if($this -> isNewRecord) { $this -> authKey = Yii::$app -> security -> generateRandomString(); } return true; } return false; } }
控制器:yii
<?php namespace app\controllers; use Yii; use yii\web\Controller; use app\models\User; use yii\web\Cookie; class UserController extends Controller { /**默認方法爲home方法 * @var string */ public $defaultAction = 'home'; /**用戶登陸,當已經登陸直接跳轉home頁面, * 不然,查看是否有訪問accessToken,若是有使用accessToken登陸,若是accessToken無效則刪除accessToken而後返回到登陸界面 * 若是沒有accessToken則使用用戶的登陸密碼登陸,驗證成功後,查看是否選擇了記住我,有則生成accessToken,下次直接使用accessToken登陸 * 登陸失敗,返回到登陸界面從新登陸。 * @return string|\yii\web\Response */ public function actionLogin() { $request = Yii::$app->request->post('User'); //若是已經登陸獲取認證identity而後進入home頁面 if (!(Yii::$app->user->isGuest)) { return $this->redirect('/?r=user/home'); } else { //若是沒有登陸,查看cookie中是否有accessToken,若是有嘗試使用accessToken登陸,accessToken登陸失敗,則刪除這個無效的accessToken $accessToken = Yii::$app->request->cookies->getValue('accessToken'); if ($accessToken !== null) { if (Yii::$app->user->loginByAccessToken($accessToken)) { return $this->redirect("/?r=user/home"); } else { Yii::$app->request->cookies->remove("accessToken"); $user = new User(['scenario' => 'login']); return $this->renderPartial('login', ['model' => $user]); } } //嘗試用戶名密碼登陸,若是驗證成功,查看是否有點擊記住我,若是有生成accessToken,下次直接accessToken登陸 $request = Yii::$app->request->post('User'); if ($request && isset($request['username']) && isset($request['password'])) { $user = User::findIdentityByUsername($request['username']); if ($user && $user->validatePassword($request['password'])) { $remeberMe = Yii::$app->request->post('remeberMe'); if ($remeberMe === 'on') { //生成訪問accessToken $user->accessToken = Yii::$app->security->generateRandomString(); $user->scenario = 'login'; $user->save(); Yii::$app->response->cookies->add(new Cookie([ 'name' => 'accessToken', 'value' => $user->accessToken, 'expire' => time() + 3600 * 24 * 7 ])); } Yii::$app->user->login($user); return $this->redirect('/?r=user/home'); } } //accessToken和帳號密碼均沒法登陸,從新返回登陸界面 $user = new User(['scenario' => 'login']); return $this->renderPartial('login', ['model' => $user]); } } /**根據用戶是否登陸,選擇跳轉頁面 * @return string|\yii\web\Response */ public function actionHome() { if (Yii::$app->user->isGuest) { return $this->redirect('/?r=user/login'); } $user = Yii::$app->user->getIdentity(); return $this->renderPartial('home', ['user' => $user]); } /**退出登陸,若是有刪除accessToken,返回登陸頁面 * @return \yii\web\Response */ public function actionLogout() { $user = Yii::$app->user->getIdentity(); Yii::$app->user->logout($user); $accessToken = Yii::$app->request->cookies->getValue('accessToken'); if ($accessToken !== null) { Yii::$app->response->cookies->remove('accessToken'); } return $this->redirect('/?r=user/login'); } /** * 註冊用戶,若是註冊成功自動進入home主頁,註冊失敗進入註冊頁面 * @return string|\yii\web\Response */ public function actionRegister() { $user = new User(['scenario' => 'register']); $request = Yii::$app->request->post(); if ($request) { if ($user->load($request) && $user->setPassword() && $user->save()) { Yii::$app->user->login($user); return $this->redirect('/?r=user/login'); } } return $this->renderPartial('register', ['model' => $user]); } }
視圖login
<?php use yii\helpers\Html; use yii\bootstrap\ActiveForm; $form = ActiveForm::begin([ 'id' => 'user', 'options' => ['class' => 'form-horizontal'], 'fieldConfig' => [ 'template' => "{label}\n<div class=\"col-lg-3\">{input}</div>\n<div class=\"col-lg-8\">{error}</div>", 'labelOptions' => ['class' => 'col-lg-1 control-label'], ], ]); ?> <?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?> <?= $form->field($model, 'password')->passwordInput() ?> <input type="checkbox" name="remeberMe" >記住我 <?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?> <?php ActiveForm::end(); ?> <a href="/?r=user/register">註冊</a>
視圖註冊:
<?php use yii\helpers\Html; use yii\bootstrap\ActiveForm; $this->title = 'register'; $form = ActiveForm::begin([ 'id' => 'login-form', 'options' => ['class' => 'form-horizontal'], 'fieldConfig' => [ 'template' => "{label}\n<div class=\"col-lg-3\">{input}</div>\n<div class=\"col-lg-8\">{error}</div>", 'labelOptions' => ['class' => 'col-lg-1 control-label'], ], ]); ?> <?= $form->field($model, 'username')->textInput(['autofocus' => true]) ?> <?= $form->field($model, 'password')->passwordInput() ?> <?= Html::submitButton('Register', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?> <?php ActiveForm::end(); ?> <a href="/?r=user/login">登陸</a>
home視圖:
<?php echo $user -> username; ?> <a href="/?r=user/logout">退出</a>
運行:
home頁面
文檔:http://www.yiichina.com/doc/guide/2.0/security-authentication