Browse Source

Finished User.

tags/2.0.0-beta
Qiang Xue 12 years ago
parent
commit
c0d0d1dc35
  1. 15
      framework/web/Controller.php
  2. 20
      framework/web/Identity.php
  3. 38
      framework/web/Response.php
  4. 197
      framework/web/User.php
  5. 2
      framework/web/UserEvent.php

15
framework/web/Controller.php

@ -41,19 +41,4 @@ class Controller extends \yii\base\Controller
} }
return Yii::$app->getUrlManager()->createUrl($route, $params); return Yii::$app->getUrlManager()->createUrl($route, $params);
} }
/**
* Redirects the browser to the specified URL or route (controller/action).
* @param mixed $url the URL to be redirected to. If the parameter is an array,
* the first element must be a route to a controller action and the rest
* are GET parameters in name-value pairs.
* @param boolean $terminate whether to terminate the current application after calling this method. Defaults to true.
* @param integer $statusCode the HTTP status code. Defaults to 302. See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html}
* for details about HTTP status code.
*/
public function redirect($url, $terminate = true, $statusCode = 302)
{
$url = Html::url($url);
Yii::$app->getResponse()->redirect($url, $terminate, $statusCode);
}
} }

20
framework/web/Identity.php

@ -15,6 +15,14 @@ namespace yii\web;
interface Identity interface Identity
{ {
/** /**
* Finds an identity by the given ID.
* @param string|integer $id the ID to be looked for
* @return Identity the identity object that matches the given ID.
* Null should be returned if such an identity cannot be found
* or the identity is not in an active state (disabled, deleted, etc.)
*/
public static function findIdentity($id);
/**
* Returns an ID that can uniquely identify a user identity. * Returns an ID that can uniquely identify a user identity.
* @return string|integer an ID that uniquely identifies a user identity. * @return string|integer an ID that uniquely identifies a user identity.
*/ */
@ -23,23 +31,19 @@ interface Identity
* Returns a key that can be used to check the validity of a given identity ID. * 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 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 returned key can be a string, an integer, or any serializable data.
*
* This is required if [[User::enableAutoLogin]] is enabled.
* @return string a key that is used to check the validity of a given identity ID. * @return string a key that is used to check the validity of a given identity ID.
* @see validateAuthKey() * @see validateAuthKey()
*/ */
public function getAuthKey(); public function getAuthKey();
/** /**
* Validates the given auth key. * Validates the given auth key.
*
* This is required if [[User::enableAutoLogin]] is enabled.
* @param string $authKey the given auth key * @param string $authKey the given auth key
* @return boolean whether the given auth key is valid. * @return boolean whether the given auth key is valid.
* @see getAuthKey() * @see getAuthKey()
*/ */
public function validateAuthKey($authKey); public function validateAuthKey($authKey);
/**
* Finds an identity by the given ID.
* @param string|integer $id the ID to be looked for
* @return Identity the identity object that matches the given ID.
* Null should be returned if such an identity cannot be found
* or the identity is not in an active state (disabled, deleted, etc.)
*/
public static function findIdentity($id);
} }

38
framework/web/Response.php

@ -9,6 +9,7 @@ namespace yii\web;
use Yii; use Yii;
use yii\helpers\FileHelper; use yii\helpers\FileHelper;
use yii\helpers\Html;
/** /**
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
@ -17,6 +18,14 @@ use yii\helpers\FileHelper;
class Response extends \yii\base\Response class Response extends \yii\base\Response
{ {
/** /**
* @var integer the HTTP status code that should be used when redirecting in AJAX mode.
* This is used by [[redirect()]]. A 2xx code should normally be used for this purpose
* so that the AJAX handler will treat the response as a success.
* @see redirect
*/
public $ajaxRedirectCode = 278;
/**
* Sends a file to user. * Sends a file to user.
* @param string $fileName file name * @param string $fileName file name
* @param string $content content to be set. * @param string $content content to be set.
@ -147,24 +156,45 @@ class Response extends \yii\base\Response
/** /**
* Redirects the browser to the specified URL. * Redirects the browser to the specified URL.
* @param string $url URL to be redirected to. Note that when URL is not * This method will send out a "Location" header to achieve the redirection.
* absolute (not starting with "/") it will be relative to current request URL. * In AJAX mode, this normally will not work as expected unless there are some
* client-side JavaScript code handling the redirection. To help achieve this goal,
* this method will use [[ajaxRedirectCode]] as the HTTP status code when performing
* redirection in AJAX mode. The following JavaScript code may be used on the client
* side to handle the redirection response:
*
* ~~~
* $(document).ajaxSuccess(function(event, xhr, settings) {
* if (xhr.status == 278) {
* window.location = xhr.getResponseHeader('Location');
* }
* });
* ~~~
*
* @param array|string $url the URL to be redirected to. [[\yii\helpers\Html::url()]]
* will be used to normalize the URL. If the resulting URL is still a relative URL
* (one without host info), the current request host info will be used.
* @param boolean $terminate whether to terminate the current application * @param boolean $terminate whether to terminate the current application
* @param integer $statusCode the HTTP status code. Defaults to 302. See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} * @param integer $statusCode the HTTP status code. Defaults to 302.
* See [[http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html]]
* for details about HTTP status code. * for details about HTTP status code.
* Note that if the request is an AJAX request, [[ajaxRedirectCode]] will be used instead.
*/ */
public function redirect($url, $terminate = true, $statusCode = 302) public function redirect($url, $terminate = true, $statusCode = 302)
{ {
$url = Html::url($url);
if (strpos($url, '/') === 0 && strpos($url, '//') !== 0) { if (strpos($url, '/') === 0 && strpos($url, '//') !== 0) {
$url = Yii::$app->getRequest()->getHostInfo() . $url; $url = Yii::$app->getRequest()->getHostInfo() . $url;
} }
if (Yii::$app->getRequest()->getIsAjaxRequest()) {
$statusCode = $this->ajaxRedirectCode;
}
header('Location: ' . $url, true, $statusCode); header('Location: ' . $url, true, $statusCode);
if ($terminate) { if ($terminate) {
Yii::$app->end(); Yii::$app->end();
} }
} }
/** /**
* Returns the cookie collection. * Returns the cookie collection.
* Through the returned cookie collection, you add or remove cookies as follows, * Through the returned cookie collection, you add or remove cookies as follows,

197
framework/web/User.php

@ -11,7 +11,6 @@ use Yii;
use yii\base\Component; use yii\base\Component;
use yii\base\HttpException; use yii\base\HttpException;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use yii\helpers\Html;
/** /**
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
@ -67,17 +66,27 @@ class User extends Component
public $authTimeout; public $authTimeout;
/** /**
* @var boolean whether to automatically renew the identity cookie each time a page is requested. * @var boolean whether to automatically renew the identity cookie each time a page is requested.
* Defaults to false. This property is effective only when {@link enableAutoLogin} is true. * This property is effective only when [[enableAutoLogin]] is true.
* When this is false, the identity cookie will expire after the specified duration since the user * When this is false, the identity cookie will expire after the specified duration since the user
* is initially logged in. When this is true, the identity cookie will expire after the specified duration * is initially logged in. When this is true, the identity cookie will expire after the specified duration
* since the user visits the site the last time. * since the user visits the site the last time.
* @see enableAutoLogin * @see enableAutoLogin
*/ */
public $autoRenewCookie = true; public $autoRenewCookie = true;
/**
* @var string the session variable name used to store the value of [[id]].
*/
public $idVar = '__id';
/**
* @var string the session variable name used to store the value of expiration timestamp of the authenticated state.
* This is used when [[authTimeout]] is set.
*/
public $authTimeoutVar = '__expire';
/**
* @var string the session variable name used to store the value of [[returnUrl]].
*/
public $returnUrlVar = '__returnUrl';
public $idSessionVar = '__id';
public $authTimeoutSessionVar = '__expire';
public $returnUrlSessionVar = '__returnUrl';
/** /**
* Initializes the application component. * Initializes the application component.
@ -108,7 +117,10 @@ class User extends Component
} }
} }
public function loadIdentity() /**
* Loads the [[identity]] object according to [[id]].
*/
protected function loadIdentity()
{ {
$id = $this->getId(); $id = $this->getId();
if ($id === null) { if ($id === null) {
@ -116,25 +128,22 @@ class User extends Component
} else { } else {
/** @var $class Identity */ /** @var $class Identity */
$class = $this->identityClass; $class = $this->identityClass;
$this->identity = $class::findIdentity($this->getId()); $this->identity = $class::findIdentity($id);
} }
} }
/** /**
* Logs in a user. * Logs in a user.
* *
* The user identity information will be saved in storage that is * This method stores the necessary session information to keep track
* persistent during the user session. By default, the storage is simply * of the user identity information. If `$duration` is greater than 0
* the session storage. If the duration parameter is greater than 0, * and [[enableAutoLogin]] is true, it will also send out an identity
* a cookie will be sent to prepare for cookie-based login in future. * cookie to support cookie-based login.
*
* Note, you have to set {@link enableAutoLogin} to true
* if you want to allow user to be authenticated based on the cookie information.
* *
* @param Identity $identity the user identity (which should already be authenticated) * @param Identity $identity the user identity (which should already be authenticated)
* @param integer $duration number of seconds that the user can remain in logged-in status. Defaults to 0, meaning login till the user closes the browser. * @param integer $duration number of seconds that the user can remain in logged-in status.
* If greater than 0, cookie-based login will be used. In this case, {@link enableAutoLogin} * Defaults to 0, meaning login till the user closes the browser or the session is manually destroyed.
* must be set true, otherwise an exception will be thrown. * If greater than 0 and [[enableAutoLogin]] is true, cookie-based login will be supported.
* @return boolean whether the user is logged in * @return boolean whether the user is logged in
*/ */
public function login($identity, $duration = 0) public function login($identity, $duration = 0)
@ -150,11 +159,10 @@ class User extends Component
} }
/** /**
* Populates the current user object with the information obtained from cookie. * Logs in a user by cookie.
* This method is used when automatic login ({@link enableAutoLogin}) is enabled. *
* The user identity information is recovered from cookie. * This method attempts to log in a user using the ID and authKey information
* Sufficient security measures are used to prevent cookie data from being tampered. * provided by the given cookie.
* @see sendIdentityCookie
*/ */
protected function loginByCookie() protected function loginByCookie()
{ {
@ -185,9 +193,8 @@ class User extends Component
/** /**
* Logs out the current user. * Logs out the current user.
* This will remove authentication-related session data. * This will remove authentication-related session data.
* If the parameter is true, the whole session will be destroyed as well. * If `$destroySession` is true, all session data will be removed.
* @param boolean $destroySession whether to destroy the whole session. Defaults to true. If false, * @param boolean $destroySession whether to destroy the whole session. Defaults to true.
* then {@link clearStates} will be called, which removes only the data stored via {@link setState}.
*/ */
public function logout($destroySession = true) public function logout($destroySession = true)
{ {
@ -215,73 +222,63 @@ class User extends Component
/** /**
* Returns a value that uniquely represents the user. * Returns a value that uniquely represents the user.
* @return mixed the unique identifier for the user. If null, it means the user is a guest. * @return string|integer the unique identifier for the user. If null, it means the user is a guest.
*/ */
public function getId() public function getId()
{ {
return Yii::$app->getSession()->get($this->idSessionVar); return Yii::$app->getSession()->get($this->idVar);
} }
/** /**
* @param mixed $value the unique identifier for the user. If null, it means the user is a guest. * @param string|integer $value the unique identifier for the user. If null, it means the user is a guest.
*/ */
public function setId($value) public function setId($value)
{ {
Yii::$app->getSession()->set($this->idSessionVar, $value); Yii::$app->getSession()->set($this->idVar, $value);
} }
/** /**
* Returns the URL that the user should be redirected to after successful login. * 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, * 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. * the action should read this property and use it to redirect the user browser.
* @param string $defaultUrl the default return URL in case it was not set previously. If this is null, * @param string|array $defaultUrl the default return URL in case it was not set previously.
* the application entry URL will be considered as the default return URL. * If this is null, it means [[Application::homeUrl]] will be redirected to.
* Please refer to [[\yii\helpers\Html::url()]] on acceptable URL formats.
* @return string the URL that the user should be redirected to after login. * @return string the URL that the user should be redirected to after login.
* @see loginRequired * @see loginRequired
*/ */
public function getReturnUrl($defaultUrl = null) public function getReturnUrl($defaultUrl = null)
{ {
$url = Yii::$app->getSession()->get($this->returnUrlSessionVar, $defaultUrl); $url = Yii::$app->getSession()->get($this->returnUrlVar, $defaultUrl);
if ($url === null) { return $url === null ? Yii::$app->getHomeUrl() : $url;
return Yii::$app->getHomeUrl();
} else {
return Html::url($url);
}
} }
/** /**
* @param string $value the URL that the user should be redirected to after login. * @param string|array $url the URL that the user should be redirected to after login.
* Please refer to [[\yii\helpers\Html::url()]] on acceptable URL formats.
*/ */
public function setReturnUrl($value) public function setReturnUrl($url)
{ {
Yii::$app->getSession()->set($this->returnUrlSessionVar, $value); Yii::$app->getSession()->set($this->returnUrlVar, $url);
} }
/** /**
* Redirects the user browser to the login page. * Redirects the user browser to the login page.
* Before the redirection, the current URL (if it's not an AJAX url) will be * Before the redirection, the current URL (if it's not an AJAX url) will be
* kept in {@link returnUrl} so that the user browser may be redirected back * kept as [[returnUrl]] so that the user browser may be redirected back
* to the current page after successful login. Make sure you set {@link loginUrl} * to the current page after successful login. Make sure you set [[loginUrl]]
* so that the user browser can be redirected to the specified login URL after * so that the user browser can be redirected to the specified login URL after
* calling this method. * calling this method.
* After calling this method, the current request processing will be terminated. * After calling this method, the current request processing will be terminated.
*/ */
public function loginRequired() public function loginRequired()
{ {
if (($url = $this->loginUrl) !== null) {
$url = Html::url($url);
$request = Yii::$app->getRequest(); $request = Yii::$app->getRequest();
if (strpos($url, '/') === 0 && strpos($url, '//') !== 0) { if (!$request->getIsAjaxRequest()) {
$url = $request->getHostInfo() . $url; $this->setReturnUrl($request->getUrl());
}
if ($request->getIsAjaxRequest()) {
echo json_encode(array(
'redirect' => $url,
));
Yii::$app->end();
} else {
Yii::$app->getResponse()->redirect($url);
} }
if ($this->loginUrl !== null) {
Yii::$app->getResponse()->redirect($this->loginUrl);
} else { } else {
throw new HttpException(403, Yii::t('yii|Login Required')); throw new HttpException(403, Yii::t('yii|Login Required'));
} }
@ -289,21 +286,18 @@ class User extends Component
/** /**
* This method is called before logging in a user. * This method is called before logging in a user.
* You may override this method to provide additional security check. * The default implementation will trigger the [[EVENT_BEFORE_LOGIN]] event.
* For example, when the login is cookie-based, you may want to verify * If you override this method, make sure you call the parent implementation
* that the user ID together with a random token in the states can be found * so that the event is triggered.
* in the database. This will prevent hackers from faking arbitrary * @param Identity $identity the user identity information
* identity cookies even if they crack down the server private key. * @param boolean $cookieBased whether the login is cookie-based
* @param mixed $id the user ID. This is the same as returned by {@link getId()}. * @return boolean whether the user should continue to be logged in
* @param array $states a set of name-value pairs that are provided by the user identity.
* @param boolean $fromCookie whether the login is based on cookie
* @return boolean whether the user should be logged in
*/ */
protected function beforeLogin($identity, $fromCookie) protected function beforeLogin($identity, $cookieBased)
{ {
$event = new UserEvent(array( $event = new UserEvent(array(
'identity' => $identity, 'identity' => $identity,
'fromCookie' => $fromCookie, 'cookieBased' => $cookieBased,
)); ));
$this->trigger(self::EVENT_BEFORE_LOGIN, $event); $this->trigger(self::EVENT_BEFORE_LOGIN, $event);
return $event->isValid; return $event->isValid;
@ -311,24 +305,27 @@ class User extends Component
/** /**
* This method is called after the user is successfully logged in. * This method is called after the user is successfully logged in.
* You may override this method to do some postprocessing (e.g. log the user * The default implementation will trigger the [[EVENT_AFTER_LOGIN]] event.
* login IP and time; load the user permission information). * If you override this method, make sure you call the parent implementation
* @param boolean $fromCookie whether the login is based on cookie. * so that the event is triggered.
* @param Identity $identity the user identity information
* @param boolean $cookieBased whether the login is cookie-based
*/ */
protected function afterLogin($identity, $fromCookie) protected function afterLogin($identity, $cookieBased)
{ {
$this->trigger(self::EVENT_AFTER_LOGIN, new UserEvent(array( $this->trigger(self::EVENT_AFTER_LOGIN, new UserEvent(array(
'identity' => $identity, 'identity' => $identity,
'fromCookie' => $fromCookie, 'cookieBased' => $cookieBased,
))); )));
} }
/** /**
* This method is invoked when calling {@link logout} to log out a user. * This method is invoked when calling [[logout()]] to log out a user.
* If this method return false, the logout action will be cancelled. * The default implementation will trigger the [[EVENT_BEFORE_LOGOUT]] event.
* You may override this method to provide additional check before * If you override this method, make sure you call the parent implementation
* logging out a user. * so that the event is triggered.
* @return boolean whether to log out the user * @param Identity $identity the user identity information
* @return boolean whether the user should continue to be logged out
*/ */
protected function beforeLogout($identity) protected function beforeLogout($identity)
{ {
@ -340,8 +337,11 @@ class User extends Component
} }
/** /**
* This method is invoked right after a user is logged out. * This method is invoked right after a user is logged out via [[logout()]].
* You may override this method to do some extra cleanup work for the user. * The default implementation will trigger the [[EVENT_AFTER_LOGOUT]] event.
* If you override this method, make sure you call the parent implementation
* so that the event is triggered.
* @param Identity $identity the user identity information
*/ */
protected function afterLogout($identity) protected function afterLogout($identity)
{ {
@ -350,7 +350,6 @@ class User extends Component
))); )));
} }
/** /**
* Renews the identity cookie. * Renews the identity cookie.
* This method will set the expiration time of the identity cookie to be the current time * This method will set the expiration time of the identity cookie to be the current time
@ -372,12 +371,12 @@ class User extends Component
} }
/** /**
* Saves necessary user data into a cookie. * Sends an identity cookie.
* This method is used when automatic login ({@link enableAutoLogin}) is enabled. * This method is used when [[enableAutoLogin]] is true.
* This method saves user ID, username, other identity states and a validation key to cookie. * It saves [[id]], [[Identity::getAuthKey()|auth key]], and the duration of cookie-based login
* These information are used to do authentication next time when user visits the application. * information in the cookie.
* @param Identity $identity * @param Identity $identity
* @param integer $duration number of seconds that the user can remain in logged-in status. Defaults to 0, meaning login till the user closes the browser. * @param integer $duration number of seconds that the user can remain in logged-in status.
* @see loginByCookie * @see loginByCookie
*/ */
protected function sendIdentityCookie($identity, $duration) protected function sendIdentityCookie($identity, $duration)
@ -394,42 +393,40 @@ class User extends Component
/** /**
* Changes the current user with the specified identity information. * Changes the current user with the specified identity information.
* This method is called by {@link login} and {@link restoreFromCookie} * This method is called by [[login()]] and [[loginByCookie()]]
* when the current user needs to be populated with the corresponding * when the current user needs to be associated with the corresponding
* identity information. Derived classes may override this method * identity information.
* by retrieving additional user-related information. Make sure the * @param Identity $identity the identity information to be associated with the current user.
* parent implementation is called first.
* @param Identity $identity a unique identifier for the user
*/ */
protected function switchIdentity($identity) protected function switchIdentity($identity)
{ {
Yii::$app->getSession()->regenerateID(true); Yii::$app->getSession()->regenerateID(true);
$this->identity = $identity; $this->identity = $identity;
$session = Yii::$app->getSession();
$session->remove($this->idVar);
$session->remove($this->authTimeoutVar);
if ($identity instanceof Identity) { if ($identity instanceof Identity) {
$this->setId($identity->getId()); $this->setId($identity->getId());
if ($this->authTimeout !== null) { if ($this->authTimeout !== null) {
Yii::$app->getSession()->set($this->authTimeoutSessionVar, time() + $this->authTimeout); Yii::$app->getSession()->set($this->authTimeoutVar, time() + $this->authTimeout);
} }
} else {
$session = Yii::$app->getSession();
$session->remove($this->idSessionVar);
$session->remove($this->authTimeoutSessionVar);
} }
} }
/** /**
* Updates the authentication status according to {@link authTimeout}. * Updates the authentication status according to [[authTimeout]].
* If the user has been inactive for {@link authTimeout} seconds, * This method is called during [[init()]].
* he will be automatically logged out. * It will update the user's authentication status if it has not outdated yet.
* Otherwise, it will logout the user.
*/ */
protected function renewAuthStatus() protected function renewAuthStatus()
{ {
if ($this->authTimeout !== null && !$this->getIsGuest()) { if ($this->authTimeout !== null && $this->identity !== null) {
$expire = Yii::$app->getSession()->get($this->authTimeoutSessionVar); $expire = Yii::$app->getSession()->get($this->authTimeoutVar);
if ($expire !== null && $expire < time()) { if ($expire !== null && $expire < time()) {
$this->logout(false); $this->logout(false);
} else { } else {
Yii::$app->getSession()->set($this->authTimeoutSessionVar, time() + $this->authTimeout); Yii::$app->getSession()->set($this->authTimeoutVar, time() + $this->authTimeout);
} }
} }
} }

2
framework/web/UserEvent.php

@ -24,7 +24,7 @@ class UserEvent extends Event
* @var boolean whether the login is cookie-based. This property is only meaningful * @var boolean whether the login is cookie-based. This property is only meaningful
* for [[User::EVENT_BEFORE_LOGIN]] and [[User::EVENT_AFTER_LOGIN]] events. * for [[User::EVENT_BEFORE_LOGIN]] and [[User::EVENT_AFTER_LOGIN]] events.
*/ */
public $fromCookie; public $cookieBased;
/** /**
* @var boolean whether the login or logout should proceed. * @var boolean whether the login or logout should proceed.
* Event handlers may modify this property to determine whether the login or logout should proceed. * Event handlers may modify this property to determine whether the login or logout should proceed.

Loading…
Cancel
Save