mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-01 03:26:36 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			407 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			407 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| Sessions and Cookies
 | |
| ====================
 | |
| 
 | |
| Sessions and cookies allow data to be persisted across multiple user requests. In plain PHP you may access them
 | |
| through the global variables `$_SESSION` and `$_COOKIE`, respectively. Yii encapsulates sessions and cookies as objects
 | |
| and thus allows you to access them in an object-oriented fashion with additional useful enhancements.
 | |
| 
 | |
| 
 | |
| ## Sessions <span id="sessions"></span>
 | |
| 
 | |
| Like [requests](runtime-requests.md) and [responses](runtime-responses.md), you can get access to sessions via
 | |
| the `session` [application component](structure-application-components.md) which is an instance of [[yii\web\Session]],
 | |
| by default.
 | |
| 
 | |
| 
 | |
| ### Opening and Closing Sessions <span id="opening-closing-sessions"></span>
 | |
| 
 | |
| To open and close a session, you can do the following:
 | |
| 
 | |
| ```php
 | |
| $session = Yii::$app->session;
 | |
| 
 | |
| // check if a session is already open
 | |
| if ($session->isActive) ...
 | |
| 
 | |
| // open a session
 | |
| $session->open();
 | |
| 
 | |
| // close a session
 | |
| $session->close();
 | |
| 
 | |
| // destroys all data registered to a session.
 | |
| $session->destroy();
 | |
| ```
 | |
| 
 | |
| You can call [[yii\web\Session::open()|open()]] and [[yii\web\Session::close()|close()]] multiple times
 | |
| without causing errors; internally the methods will first check if the session is already open.
 | |
| 
 | |
| 
 | |
| ### Accessing Session Data <span id="access-session-data"></span>
 | |
| 
 | |
| To access the data stored in session, you can do the following:
 | |
| 
 | |
| ```php
 | |
| $session = Yii::$app->session;
 | |
| 
 | |
| // get a session variable. The following usages are equivalent:
 | |
| $language = $session->get('language');
 | |
| $language = $session['language'];
 | |
| $language = isset($_SESSION['language']) ? $_SESSION['language'] : null;
 | |
| 
 | |
| // set a session variable. The following usages are equivalent:
 | |
| $session->set('language', 'en-US');
 | |
| $session['language'] = 'en-US';
 | |
| $_SESSION['language'] = 'en-US';
 | |
| 
 | |
| // remove a session variable. The following usages are equivalent:
 | |
| $session->remove('language');
 | |
| unset($session['language']);
 | |
| unset($_SESSION['language']);
 | |
| 
 | |
| // check if a session variable exists. The following usages are equivalent:
 | |
| if ($session->has('language')) ...
 | |
| if (isset($session['language'])) ...
 | |
| if (isset($_SESSION['language'])) ...
 | |
| 
 | |
| // traverse all session variables. The following usages are equivalent:
 | |
| foreach ($session as $name => $value) ...
 | |
| foreach ($_SESSION as $name => $value) ...
 | |
| ```
 | |
| 
 | |
| > Info: When you access session data through the `session` component, a session will be automatically opened
 | |
| if it has not been done so before. This is different from accessing session data through `$_SESSION`, which requires
 | |
| an explicit call of `session_start()`.
 | |
| 
 | |
| When working with session data that are arrays, the `session` component has a limitation which prevents you from
 | |
| directly modifying an array element. For example,
 | |
| 
 | |
| ```php
 | |
| $session = Yii::$app->session;
 | |
| 
 | |
| // the following code will NOT work
 | |
| $session['captcha']['number'] = 5;
 | |
| $session['captcha']['lifetime'] = 3600;
 | |
| 
 | |
| // the following code works:
 | |
| $session['captcha'] = [
 | |
|     'number' => 5,
 | |
|     'lifetime' => 3600,
 | |
| ];
 | |
| 
 | |
| // the following code also works:
 | |
| echo $session['captcha']['lifetime'];
 | |
| ```
 | |
| 
 | |
| You can use one of the following workarounds to solve this problem:
 | |
| 
 | |
| ```php
 | |
| $session = Yii::$app->session;
 | |
| 
 | |
| // directly use $_SESSION (make sure Yii::$app->session->open() has been called)
 | |
| $_SESSION['captcha']['number'] = 5;
 | |
| $_SESSION['captcha']['lifetime'] = 3600;
 | |
| 
 | |
| // get the whole array first, modify it and then save it back
 | |
| $captcha = $session['captcha'];
 | |
| $captcha['number'] = 5;
 | |
| $captcha['lifetime'] = 3600;
 | |
| $session['captcha'] = $captcha;
 | |
| 
 | |
| // use ArrayObject instead of array
 | |
| $session['captcha'] = new \ArrayObject;
 | |
| ...
 | |
| $session['captcha']['number'] = 5;
 | |
| $session['captcha']['lifetime'] = 3600;
 | |
| 
 | |
| // store array data by keys with a common prefix
 | |
| $session['captcha.number'] = 5;
 | |
| $session['captcha.lifetime'] = 3600;
 | |
| ```
 | |
| 
 | |
| For better performance and code readability, we recommend the last workaround. That is, instead of storing
 | |
| an array as a single session variable, you store each array element as a session variable which shares the same
 | |
| key prefix with other array elements.
 | |
| 
 | |
| 
 | |
| ### Custom Session Storage <span id="custom-session-storage"></span>
 | |
| 
 | |
| The default [[yii\web\Session]] class stores session data as files on the server. Yii also provides the following
 | |
| session classes implementing different session storage:
 | |
| 
 | |
| * [[yii\web\DbSession]]: stores session data in a database table.
 | |
| * [[yii\web\CacheSession]]: stores session data in a cache with the help of a configured [cache component](caching-data.md#cache-components).
 | |
| * [[yii\redis\Session]]: stores session data using [redis](http://redis.io/) as the storage medium.
 | |
| * [[yii\mongodb\Session]]: stores session data in a [MongoDB](https://www.mongodb.com/).
 | |
| 
 | |
| All these session classes support the same set of API methods. As a result, you can switch to a different
 | |
| session storage class without the need to modify your application code that uses sessions.
 | |
| 
 | |
| > Note: If you want to access session data via `$_SESSION` while using custom session storage, you must make
 | |
|   sure that the session has already been started by [[yii\web\Session::open()]]. This is because custom session storage
 | |
|   handlers are registered within this method.
 | |
| 
 | |
| > Note: If you use a custom session storage you may need to configure the session garbage collector explicitly.
 | |
|   Some installations of PHP (e.g. Debian) use a garbage collector probability of 0 and clean session files
 | |
|   offline in a cronjob. This process does not apply to your custom storage so you need to configure
 | |
|   [[yii\web\Session::$GCProbability]] to use a non-zero value.
 | |
| 
 | |
| To learn how to configure and use these component classes, please refer to their API documentation. Below is
 | |
| an example showing how to configure [[yii\web\DbSession]] in the application configuration to use a database table
 | |
| for session storage:
 | |
| 
 | |
| ```php
 | |
| return [
 | |
|     'components' => [
 | |
|         'session' => [
 | |
|             'class' => 'yii\web\DbSession',
 | |
|             // 'db' => 'mydb',  // the application component ID of the DB connection. Defaults to 'db'.
 | |
|             // 'sessionTable' => 'my_session', // session table name. Defaults to 'session'.
 | |
|         ],
 | |
|     ],
 | |
| ];
 | |
| ```
 | |
| 
 | |
| You also need to create the following database table to store session data:
 | |
| 
 | |
| ```sql
 | |
| CREATE TABLE session
 | |
| (
 | |
|     id CHAR(40) NOT NULL PRIMARY KEY,
 | |
|     expire INTEGER,
 | |
|     data BLOB
 | |
| )
 | |
| ```
 | |
| 
 | |
| where 'BLOB' refers to the BLOB-type of your preferred DBMS. Below are the BLOB types that can be used for some popular DBMS:
 | |
| 
 | |
| - MySQL: LONGBLOB
 | |
| - PostgreSQL: BYTEA
 | |
| - MSSQL: BLOB
 | |
| 
 | |
| > Note: According to the php.ini setting of `session.hash_function`, you may need to adjust
 | |
|   the length of the `id` column. For example, if `session.hash_function=sha256`, you should use a
 | |
|   length 64 instead of 40.
 | |
| 
 | |
| Alternatively, this can be accomplished with the following migration:
 | |
| 
 | |
| ```php
 | |
| <?php
 | |
| 
 | |
| use yii\db\Migration;
 | |
| 
 | |
| class m170529_050554_create_table_session extends Migration
 | |
| {
 | |
|     public function up()
 | |
|     {
 | |
|         $this->createTable('{{%session}}', [
 | |
|             'id' => $this->char(64)->notNull(),
 | |
|             'expire' => $this->integer(),
 | |
|             'data' => $this->binary()
 | |
|         ]);
 | |
|         $this->addPrimaryKey('pk-id', '{{%session}}', 'id');
 | |
|     }
 | |
| 
 | |
|     public function down()
 | |
|     {
 | |
|         $this->dropTable('{{%session}}');
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| ### Flash Data <span id="flash-data"></span>
 | |
| 
 | |
| Flash data is a special kind of session data which, once set in one request, will only be available during
 | |
| the next request and will be automatically deleted afterwards. Flash data is most commonly used to implement
 | |
| messages that should only be displayed to end users once, such as a confirmation message displayed after
 | |
| a user successfully submits a form.
 | |
| 
 | |
| You can set and access flash data through the `session` application component. For example,
 | |
| 
 | |
| ```php
 | |
| $session = Yii::$app->session;
 | |
| 
 | |
| // Request #1
 | |
| // set a flash message named as "postDeleted"
 | |
| $session->setFlash('postDeleted', 'You have successfully deleted your post.');
 | |
| 
 | |
| // Request #2
 | |
| // display the flash message named "postDeleted"
 | |
| echo $session->getFlash('postDeleted');
 | |
| 
 | |
| // Request #3
 | |
| // $result will be false since the flash message was automatically deleted
 | |
| $result = $session->hasFlash('postDeleted');
 | |
| ```
 | |
| 
 | |
| Like regular session data, you can store arbitrary data as flash data.
 | |
| 
 | |
| When you call [[yii\web\Session::setFlash()]], it will overwrite any existing flash data that has the same name.
 | |
| To append new flash data to an existing message of the same name, you may call [[yii\web\Session::addFlash()]] instead.
 | |
| For example:
 | |
| 
 | |
| ```php
 | |
| $session = Yii::$app->session;
 | |
| 
 | |
| // Request #1
 | |
| // add a few flash messages under the name of "alerts"
 | |
| $session->addFlash('alerts', 'You have successfully deleted your post.');
 | |
| $session->addFlash('alerts', 'You have successfully added a new friend.');
 | |
| $session->addFlash('alerts', 'You are promoted.');
 | |
| 
 | |
| // Request #2
 | |
| // $alerts is an array of the flash messages under the name of "alerts"
 | |
| $alerts = $session->getFlash('alerts');
 | |
| ```
 | |
| 
 | |
| > Note: Try not to use [[yii\web\Session::setFlash()]] together with [[yii\web\Session::addFlash()]] for flash data
 | |
|   of the same name. This is because the latter method will automatically turn the flash data into an array so that it
 | |
|   can append new flash data of the same name. As a result, when you call [[yii\web\Session::getFlash()]], you may
 | |
|   find sometimes you are getting an array while sometimes you are getting a string, depending on the order of
 | |
|   the invocation of these two methods.
 | |
| 
 | |
| > Tip: For displaying Flash messages you can use [[yii\bootstrap\Alert|bootstrap Alert]] widget in the following way:
 | |
| >
 | |
| > ```php
 | |
| > echo Alert::widget([
 | |
| >    'options' => ['class' => 'alert-info'],
 | |
| >    'body' => Yii::$app->session->getFlash('postDeleted'),
 | |
| > ]);
 | |
| > ```
 | |
| 
 | |
| 
 | |
| ## Cookies <span id="cookies"></span>
 | |
| 
 | |
| Yii represents each cookie as an object of [[yii\web\Cookie]]. Both [[yii\web\Request]] and [[yii\web\Response]]
 | |
| maintain a collection of cookies via the property named `cookies`. The cookie collection in the former represents
 | |
| the cookies submitted in a request, while the cookie collection in the latter represents the cookies that are to
 | |
| be sent to the user.
 | |
| 
 | |
| The part of the application dealing with request and response directly is controller. Therefore, cookies should be
 | |
| read and sent in controller.
 | |
| 
 | |
| ### Reading Cookies <span id="reading-cookies"></span>
 | |
| 
 | |
| You can get the cookies in the current request using the following code:
 | |
| 
 | |
| ```php
 | |
| // get the cookie collection (yii\web\CookieCollection) from the "request" component
 | |
| $cookies = Yii::$app->request->cookies;
 | |
| 
 | |
| // get the "language" cookie value. If the cookie does not exist, return "en" as the default value.
 | |
| $language = $cookies->getValue('language', 'en');
 | |
| 
 | |
| // an alternative way of getting the "language" cookie value
 | |
| if (($cookie = $cookies->get('language')) !== null) {
 | |
|     $language = $cookie->value;
 | |
| }
 | |
| 
 | |
| // you may also use $cookies like an array
 | |
| if (isset($cookies['language'])) {
 | |
|     $language = $cookies['language']->value;
 | |
| }
 | |
| 
 | |
| // check if there is a "language" cookie
 | |
| if ($cookies->has('language')) ...
 | |
| if (isset($cookies['language'])) ...
 | |
| ```
 | |
| 
 | |
| 
 | |
| ### Sending Cookies <span id="sending-cookies"></span>
 | |
| 
 | |
| You can send cookies to end users using the following code:
 | |
| 
 | |
| ```php
 | |
| // get the cookie collection (yii\web\CookieCollection) from the "response" component
 | |
| $cookies = Yii::$app->response->cookies;
 | |
| 
 | |
| // add a new cookie to the response to be sent
 | |
| $cookies->add(new \yii\web\Cookie([
 | |
|     'name' => 'language',
 | |
|     'value' => 'zh-CN',
 | |
| ]));
 | |
| 
 | |
| // remove a cookie
 | |
| $cookies->remove('language');
 | |
| // equivalent to the following
 | |
| unset($cookies['language']);
 | |
| ```
 | |
| 
 | |
| Besides the [[yii\web\Cookie::name|name]], [[yii\web\Cookie::value|value]] properties shown in the above
 | |
| examples, the [[yii\web\Cookie]] class also defines other properties to fully represent all available cookie 
 | |
| information, such as [[yii\web\Cookie::domain|domain]], [[yii\web\Cookie::expire|expire]]. You may configure these
 | |
| properties as needed to prepare a cookie and then add it to the response's cookie collection.
 | |
| 
 | |
| ### Cookie Validation <span id="cookie-validation"></span>
 | |
| 
 | |
| When you are reading and sending cookies through the `request` and `response` components as shown in the last
 | |
| two subsections, you enjoy the added security of cookie validation which protects cookies from being modified
 | |
| on the client-side. This is achieved by signing each cookie with a hash string, which allows the application to
 | |
| tell if a cookie has been modified on the client-side. If so, the cookie will NOT be accessible through the
 | |
| [[yii\web\Request::cookies|cookie collection]] of the `request` component.
 | |
| 
 | |
| > Note: Cookie validation only protects cookie values from being modified. If a cookie fails the validation, 
 | |
| you may still access it through `$_COOKIE`. This is because third-party libraries may manipulate cookies 
 | |
| in their own way, which does not involve cookie validation.
 | |
| 
 | |
| Cookie validation is enabled by default. You can disable it by setting the [[yii\web\Request::enableCookieValidation]]
 | |
| property to be `false`, although we strongly recommend you do not do so.
 | |
| 
 | |
| > Note: Cookies that are directly read/sent via `$_COOKIE` and `setcookie()` will NOT be validated.
 | |
| 
 | |
| When using cookie validation, you must specify a [[yii\web\Request::cookieValidationKey]] that will be used to generate
 | |
| the aforementioned hash strings. You can do so by configuring the `request` component in the application configuration:
 | |
| 
 | |
| ```php
 | |
| return [
 | |
|     'components' => [
 | |
|         'request' => [
 | |
|             'cookieValidationKey' => 'fill in a secret key here',
 | |
|         ],
 | |
|     ],
 | |
| ];
 | |
| ```
 | |
| 
 | |
| > Info: [[yii\web\Request::cookieValidationKey|cookieValidationKey]] is critical to your application's security.
 | |
|   It should only be known to people you trust. Do not store it in the version control system.
 | |
|   
 | |
| ## Security settings
 | |
| 
 | |
| Both [[yii\web\Cookie]] and [[yii\web\Session]] support the following security flags:
 | |
| 
 | |
| ### httpOnly
 | |
| 
 | |
| For better security, the default value of [[yii\web\Cookie::httpOnly]] and the 'httponly' parameter of 
 | |
| [[yii\web\Session::cookieParams]] is set to `true`. This helps mitigate the risk of a client-side script accessing 
 | |
| the protected cookie (if the browser supports it).
 | |
| You may read the [HttpOnly wiki article](https://owasp.org/www-community/HttpOnly) for more details.
 | |
| 
 | |
| ### secure
 | |
| 
 | |
| The purpose of the secure flag is to prevent cookies from being sent in clear text. If the browser supports the
 | |
| secure flag it will only include the cookie when the request is sent over a secure (TLS) connection.
 | |
| You may read the [SecureFlag wiki article](https://owasp.org/www-community/controls/SecureCookieAttribute) for more details.
 | |
| 
 | |
| ### sameSite
 | |
| 
 | |
| Starting with Yii 2.0.21 the [[yii\web\Cookie::sameSite]] setting is supported. It requires PHP version 7.3.0 or higher.
 | |
| The purpose of the `sameSite` setting is to prevent CSRF (Cross-Site Request Forgery) attacks.
 | |
| If the browser supports the `sameSite` setting it will only include the cookie according to the specified policy ('Lax' or 'Strict').
 | |
| You may read the [SameSite wiki article](https://www.owasp.org/index.php/SameSite) for more details.
 | |
| For better security, an exception will be thrown if `sameSite` is used with an unsupported version of PHP.
 | |
| To use this feature across different PHP versions check the version first. E.g.
 | |
| ```php
 | |
| [
 | |
|     'sameSite' => PHP_VERSION_ID >= 70300 ? yii\web\Cookie::SAME_SITE_LAX : null,
 | |
| ]
 | |
| ```
 | |
| > Note: Since not all browsers support the `sameSite` setting yet, it is still strongly recommended to also include
 | |
|   [additional CSRF protection](security-best-practices.md#avoiding-csrf).
 | |
| 
 | |
| ## Session php.ini settings
 | |
| 
 | |
| As [noted in PHP manual](https://www.php.net/manual/en/session.security.ini.php), `php.ini` has important
 | |
| session security settings. Please ensure recommended settings are applied. Especially `session.use_strict_mode`
 | |
| that is not enabled by default in PHP installations.
 | |
| This setting can also be set with [[yii\web\Session::useStrictMode]].
 | 
