From f69a73baf2bf377fbba09268335aec8228c35413 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Fri, 29 Mar 2013 13:31:08 -0400 Subject: [PATCH] refactored User and Identity classes. --- framework/web/Identity.php | 36 +++++++++++++- framework/web/User.php | 115 ++++++++++++++++++++++++--------------------- 2 files changed, 96 insertions(+), 55 deletions(-) diff --git a/framework/web/Identity.php b/framework/web/Identity.php index 89d4282..6d67bc0 100644 --- a/framework/web/Identity.php +++ b/framework/web/Identity.php @@ -8,6 +8,35 @@ namespace yii\web; /** + * Identity is the interface that should be implemented by a class providing identity information. + * + * This interface can typically be implemented by a user model class. For example, the following + * code shows how to implement this interface by a User ActiveRecord class: + * + * ~~~ + * class User extends ActiveRecord implements Identity + * { + * public static function findIdentity($id) + * { + * return static::find($id); + * } + * + * public function getId() + * { + * return $this->id; + * } + * + * public function getAuthKey() + * { + * return $this->authKey; + * } + * + * public function validateAuthKey($authKey) + * { + * return $this->authKey === $authKey; + * } + * } + * ~~~ * * @author Qiang Xue * @since 2.0 @@ -29,8 +58,11 @@ interface Identity public function getId(); /** * Returns a key that can be used to check the validity of a given identity ID. - * The space of such keys should be big and random enough to defeat potential identity attacks. - * The returned key can be a string, an integer, or any serializable data. + * + * The key should be unique for each individual user, and should be persistent + * so that it can be used to check the validity of the user identity. + * + * The space of such keys should be big enough to defeat potential identity attacks. * * This is required if [[User::enableAutoLogin]] is enabled. * @return string a key that is used to check the validity of a given identity ID. diff --git a/framework/web/User.php b/framework/web/User.php index 5b5f977..4dc2607 100644 --- a/framework/web/User.php +++ b/framework/web/User.php @@ -13,7 +13,7 @@ use yii\base\HttpException; use yii\base\InvalidConfigException; /** - * User is an application component that manages the user authentication status. + * User is the class for the "user" application component that manages the user authentication status. * * In particular, [[User::isGuest]] returns a value indicating whether the current user is a guest or not. * Through methods [[login()]] and [[logout()]], you can change the user authentication status. @@ -32,15 +32,6 @@ class User extends Component const EVENT_AFTER_LOGOUT = 'afterLogout'; /** - * @var Identity the identity object associated with the currently logged user. - * This property is set automatically be the User component. Do not modify it directly - * unless you understand the consequence. You should normally use [[login()]], [[logout()]], - * or [[switchIdentity()]] to update the identity associated with the current user. - * - * If this property is null, it means the current user is a guest (not authenticated). - */ - public $identity; - /** * @var string the class name of the [[identity]] object. */ public $identityClass; @@ -65,7 +56,7 @@ class User extends Component * @var array the configuration of the identity cookie. This property is used only when [[enableAutoLogin]] is true. * @see Cookie */ - public $identityCookie = array('name' => '__identity'); + public $identityCookie = array('name' => '__identity', 'httponly' => true); /** * @var integer the number of seconds in which the user will be logged out automatically if he * remains inactive. If this property is not set, the user will be logged out after @@ -112,8 +103,6 @@ class User extends Component Yii::$app->getSession()->open(); - $this->loadIdentity(); - $this->renewAuthStatus(); if ($this->enableAutoLogin) { @@ -125,19 +114,43 @@ class User extends Component } } + private $_identity = false; + /** - * Loads the [[identity]] object according to [[id]]. + * Returns the identity object associated with the currently logged user. + * @return Identity the identity object associated with the currently logged user. + * Null is returned if the user is not logged in (not authenticated). + * @see login + * @see logout */ - protected function loadIdentity() + public function getIdentity() { - $id = $this->getId(); - if ($id === null) { - $this->identity = null; - } else { - /** @var $class Identity */ - $class = $this->identityClass; - $this->identity = $class::findIdentity($id); + if ($this->_identity === false) { + $id = $this->getId(); + if ($id === null) { + $this->_identity = null; + } else { + /** @var $class Identity */ + $class = $this->identityClass; + $this->_identity = $class::findIdentity($id); + } } + return $this->_identity; + } + + /** + * Sets the identity object. + * This method should be mainly be used by the User component or its child class + * to maintain the identity object. + * + * You should normally update the user identity via methods [[login()]], [[logout()]] + * or [[switchIdentity()]]. + * + * @param Identity $identity the identity object associated with the currently logged user. + */ + public function setIdentity($identity) + { + $this->_identity = $identity; } /** @@ -157,10 +170,7 @@ class User extends Component public function login($identity, $duration = 0) { if ($this->beforeLogin($identity, false)) { - $this->switchIdentity($identity); - if ($duration > 0 && $this->enableAutoLogin) { - $this->sendIdentityCookie($identity, $duration); - } + $this->switchIdentity($identity, $duration); $this->afterLogin($identity, false); } return !$this->getIsGuest(); @@ -185,10 +195,7 @@ class User extends Component $identity = $class::findIdentity($id); if ($identity !== null && $identity->validateAuthKey($authKey)) { if ($this->beforeLogin($identity, true)) { - $this->switchIdentity($identity); - if ($this->autoRenewCookie) { - $this->sendIdentityCookie($identity, $duration); - } + $this->switchIdentity($identity, $this->autoRenewCookie ? $duration : 0); $this->afterLogin($identity, true); } } elseif ($identity !== null) { @@ -206,12 +213,9 @@ class User extends Component */ public function logout($destroySession = true) { - $identity = $this->identity; + $identity = $this->getIdentity(); if ($identity !== null && $this->beforeLogout($identity)) { $this->switchIdentity(null); - if ($this->enableAutoLogin) { - Yii::$app->getResponse()->getCookies()->remove(new Cookie($this->identityCookie)); - } if ($destroySession) { Yii::$app->getSession()->destroy(); } @@ -225,7 +229,7 @@ class User extends Component */ public function getIsGuest() { - return $this->identity === null; + return $this->getIdentity() === null; } /** @@ -238,14 +242,6 @@ class User extends Component } /** - * @param string|integer $value the unique identifier for the user. If null, it means the user is a guest. - */ - public function setId($value) - { - Yii::$app->getSession()->set($this->idVar, $value); - } - - /** * Returns the URL that the user should be redirected to after successful login. * This property is usually used by the login action. If the login is successful, * the action should read this property and use it to redirect the user browser. @@ -400,24 +396,37 @@ class User extends Component } /** - * Changes the current user with the specified identity information. - * This method is called by [[login()]] and [[loginByCookie()]] - * when the current user needs to be associated with the corresponding - * identity information. + * Switches to a new identity for the current user. + * + * This method will save necessary session information to keep track of the user authentication status. + * If `$duration` is provided, it will also send out appropriate identity cookie + * to support cookie-based login. + * + * This method is mainly called by [[login()]], [[logout()]] and [[loginByCookie()]] + * when the current user needs to be associated with the corresponding identity information. + * * @param Identity $identity the identity information to be associated with the current user. + * If null, it means switching 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. */ - protected function switchIdentity($identity) + public function switchIdentity($identity, $duration = 0) { - Yii::$app->getSession()->regenerateID(true); - $this->identity = $identity; $session = Yii::$app->getSession(); + $session->regenerateID(true); + $this->setIdentity($identity); $session->remove($this->idVar); $session->remove($this->authTimeoutVar); if ($identity instanceof Identity) { - $this->setId($identity->getId()); + $session->set($this->idVar, $identity->getId()); if ($this->authTimeout !== null) { - Yii::$app->getSession()->set($this->authTimeoutVar, time() + $this->authTimeout); + $session->set($this->authTimeoutVar, time() + $this->authTimeout); + } + if ($duration > 0 && $this->enableAutoLogin) { + $this->sendIdentityCookie($identity, $duration); } + } elseif ($this->enableAutoLogin) { + Yii::$app->getResponse()->getCookies()->remove(new Cookie($this->identityCookie)); } } @@ -429,7 +438,7 @@ class User extends Component */ protected function renewAuthStatus() { - if ($this->authTimeout !== null && $this->identity !== null) { + if ($this->authTimeout !== null && !$this->getIsGuest()) { $expire = Yii::$app->getSession()->get($this->authTimeoutVar); if ($expire !== null && $expire < time()) { $this->logout(false);