会话和 Cookie 允许数据在多个用户请求之间持久化。在纯 PHP 中,您可以分别通过全局变量 $_SESSION
和 $_COOKIE
访问它们。Yii 将会话和 Cookie 封装为对象,因此允许您以面向对象的方式访问它们,并提供其他有用的增强功能。
与 请求 和 响应 一样,您可以通过 session
应用程序组件 访问会话,该组件默认情况下是 yii\web\Session 的一个实例。
要打开和关闭会话,您可以执行以下操作
$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();
您可以多次调用 open() 和 close() 而不导致错误;在内部,这些方法将首先检查会话是否已打开。
要访问存储在会话中的数据,您可以执行以下操作
$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) ...
信息: 当您通过
session
组件访问会话数据时,如果会话尚未打开,则会自动打开它。这与通过$_SESSION
访问会话数据不同,后者需要显式调用session_start()
。
当使用作为数组的会话数据时,session
组件存在一个限制,它阻止您直接修改数组元素。例如,
$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'];
您可以使用以下解决方法之一来解决此问题
$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;
为了获得更好的性能和代码可读性,我们推荐最后一种解决方法。也就是说,不要将数组存储为单个会话变量,而是将每个数组元素存储为一个会话变量,这些变量与其他数组元素共享相同的键前缀。
默认的 yii\web\Session 类将会话数据存储为服务器上的文件。Yii 还提供以下会话类,实现不同的会话存储
所有这些会话类都支持相同的 API 方法集。因此,您可以切换到不同的会话存储类,而无需修改使用会话的应用程序代码。
注意:如果您希望在使用自定义会话存储时通过
$_SESSION
访问会话数据,则必须确保会话已由 yii\web\Session::open() 启动。这是因为自定义会话存储处理程序是在此方法中注册的。
注意:如果您使用自定义会话存储,则可能需要显式配置会话垃圾回收器。某些 PHP 安装(例如 Debian)使用 0 的垃圾回收概率,并在 cronjob 中脱机清理会话文件。此过程不适用于您的自定义存储,因此您需要配置 yii\web\Session::$GCProbability 以使用非零值。
要了解如何配置和使用这些组件类,请参阅其 API 文档。以下是一个示例,展示了如何在应用程序配置中配置 yii\web\DbSession 以使用数据库表进行会话存储
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'.
],
],
];
您还需要创建以下数据库表来存储会话数据
CREATE TABLE session
(
id CHAR(40) NOT NULL PRIMARY KEY,
expire INTEGER,
data BLOB
)
其中“BLOB”指的是您首选 DBMS 的 BLOB 类型。以下是可用于一些流行 DBMS 的 BLOB 类型
注意:根据
php.ini
中session.hash_function
的设置,您可能需要调整id
列的长度。例如,如果session.hash_function=sha256
,则应使用长度 64 而不是 40。
或者,可以通过以下迁移来实现
<?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}}');
}
}
闪存数据是一种特殊的会话数据,它在一次请求中设置后,仅在下一次请求中可用,并在之后自动删除。闪存数据最常用于实现仅应向最终用户显示一次的消息,例如用户成功提交表单后显示的确认消息。
您可以通过 session
应用程序组件设置和访问闪存数据。例如,
$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');
像常规会话数据一样,您可以将任意数据存储为闪存数据。
当您调用 yii\web\Session::setFlash() 时,它将覆盖具有相同名称的任何现有闪存数据。要将新的闪存数据附加到具有相同名称的现有消息,您可以改为调用 yii\web\Session::addFlash()。例如
$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');
注意:请勿将 yii\web\Session::setFlash() 与 yii\web\Session::addFlash() 结合使用以获取相同名称的闪存数据。这是因为后者方法会自动将闪存数据转换为数组,以便它可以附加相同名称的新闪存数据。因此,当您调用 yii\web\Session::getFlash() 时,您可能会发现有时您获得的是数组,而有时您获得的是字符串,具体取决于这两个方法调用的顺序。
提示:要显示闪存消息,您可以按照以下方式使用 yii\bootstrap\Alert 小部件
echo Alert::widget([ 'options' => ['class' => 'alert-info'], 'body' => Yii::$app->session->getFlash('postDeleted'), ]);
Yii 将每个 Cookie 表示为 yii\web\Cookie 的对象。 yii\web\Request 和 yii\web\Response 都通过名为 cookies
的属性维护 Cookie 集合。前者中的 Cookie 集合表示请求中提交的 Cookie,而后者中的 Cookie 集合表示要发送给用户的 Cookie。
直接处理请求和响应的应用程序部分是控制器。因此,Cookie 应该在控制器中读取和发送。
您可以使用以下代码获取当前请求中的 Cookie
// 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'])) ...
您可以使用以下代码将 Cookie 发送给最终用户
// 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']);
除了上述示例中显示的 name、value 属性之外,yii\web\Cookie 类还定义了其他属性来完整表示所有可用的 Cookie 信息,例如 domain、expire。您可以根据需要配置这些属性以准备 Cookie,然后将其添加到响应的 Cookie 集合中。
当您通过 request
和 response
组件读取和发送 Cookie 时(如上一节中的两个小节所示),您可以享受 Cookie 验证带来的额外安全性,该验证可以防止 Cookie 在客户端被修改。这是通过使用哈希字符串对每个 Cookie 进行签名来实现的,这允许应用程序判断 Cookie 是否在客户端被修改。如果是,则无法通过 request
组件的 cookie 集合 访问该 Cookie。
注意:Cookie 验证仅保护 Cookie 值不被修改。如果 Cookie 未通过验证,您仍然可以通过
$_COOKIE
访问它。这是因为第三方库可能会以自己的方式操作 Cookie,这不会涉及 Cookie 验证。
Cookie 验证默认启用。您可以通过将 yii\web\Request::$enableCookieValidation 属性设置为 false
来禁用它,尽管我们强烈建议您不要这样做。
注意:通过
$_COOKIE
和setcookie()
直接读取/发送的 Cookie 不会被验证。
在使用 Cookie 验证时,必须指定一个 yii\web\Request::$cookieValidationKey,该键将用于生成上述哈希字符串。您可以通过在应用程序配置中配置 request
组件来执行此操作
return [
'components' => [
'request' => [
'cookieValidationKey' => 'fill in a secret key here',
],
],
];
信息:cookieValidationKey 对应用程序的安全性至关重要。它只能为您信任的人所知。不要将其存储在版本控制系统中。
yii\web\Cookie 和 yii\web\Session 都支持以下安全标志
为了提高安全性,yii\web\Cookie::$httpOnly 的默认值和 yii\web\Session::$cookieParams 的“httponly”参数都设置为 true
。这有助于降低客户端脚本访问受保护的 Cookie 的风险(如果浏览器支持)。您可以阅读 HttpOnly wiki 文章 以了解更多详细信息。
secure 标志的目的是防止 Cookie 以明文形式发送。如果浏览器支持 secure 标志,它只会在线程安全(TLS)连接发送请求时包含 Cookie。您可以阅读 SecureFlag wiki 文章 以了解更多详细信息。
从 Yii 2.0.21 开始,支持 yii\web\Cookie::$sameSite 设置。它需要 PHP 7.3.0 或更高版本。sameSite
设置的目的是防止 CSRF(跨站点请求伪造)攻击。如果浏览器支持 sameSite
设置,它将根据指定的策略(“Lax”或“Strict”)包含 Cookie。您可以阅读 SameSite wiki 文章 以了解更多详细信息。为了提高安全性,如果在不受支持的 PHP 版本中使用 sameSite
,则会抛出异常。要在不同 PHP 版本中使用此功能,请先检查版本。例如:
[
'sameSite' => PHP_VERSION_ID >= 70300 ? yii\web\Cookie::SAME_SITE_LAX : null,
]
注意:由于并非所有浏览器都支持
sameSite
设置,因此仍然强烈建议您还包括 其他 CSRF 保护。
如 PHP 手册中所述,php.ini
具有重要的会话安全设置。请确保应用推荐的设置。尤其是 session.use_strict_mode
,它在 PHP 安装中默认未启用。此设置也可以使用 yii\web\Session::$useStrictMode 进行设置。
发现错别字或您认为此页面需要改进?
在 github 上编辑它 !
对于 MSSQL,文档中指出的迁移目前 **不起作用**,我的意思是,它会生成与错误转换相关的错误(char(64) 等)。您应该改用
@vendor/yiisoft/yii2/web/migrations
中的迁移。其当前内容为
<?php use yii\db\Migration; /** * Class m180611_154126_criar_tabela_sessoes */ class m180611_154126_criar_tabela_sessoes extends Migration { public function safeUp() { $dataType = $this->binary(); $tableOptions = null; switch ($this->db->driverName) { case 'mysql': // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; break; case 'sqlsrv': case 'mssql': case 'dblib': $dataType = $this->text(); break; } $this->createTable('{{%SSL_SESSOES}}', [ 'id' => $this->string()->notNull(), 'expire' => $this->integer(), 'data' => $dataType, 'PRIMARY KEY ([[id]])', ], $tableOptions); } /** * {@inheritdoc} */ public function safeDown() { $this->dropTable('{{%SSL_SESSOES}}'); } }
如果您想从基于文件的会话存储迁移到基于数据库的会话存储,则可以在 Linux 机器上使用此脚本(会话存储在
/tmp
中)将现有会话移动到数据库中,以便在您进行转换时,现有访问者不会丢失其会话。$tableName = 'system_sessions'; foreach (\yii\helpers\FileHelper::findFiles('/tmp', ['only' => ['sess_*'], 'recursive' => false]) as $file) { $sessionID = substr(basename($file), 5); $sessionExpire = filemtime($file) + 86400; $sessionData = file_get_contents($file); $affectedRows = \Yii::$app->db->createCommand()->upsert($tableName, [ 'id' => $sessionID, 'expire' => $sessionExpire, 'data' => $sessionData, ], [ 'expire' => $sessionExpire, 'data' => $sessionData, ])->execute(); echo $sessionID .' :: '. date('Y-m-d H:i', $sessionExpire) .' ::: '. $sessionData . PHP_EOL; }
如果您由于标识 Cookie 而遇到登录问题,对于 PHP 版本 < 7.3,您可以将 sameSite 属性的值设置为 None,如下所示:
**'identityCookie' => [
**'name' => 'name', 'httpOnly' => true** 'path' => '/;SameSite=None', 'secure' => true
]**
对于会话 Cookie,请修改 Cookie 参数,如下所示:
*'cookieParams' => [
'lifetime' => time()60,
'httpOnly' => true, 'secure'=>true, 'path' => '/;SameSite=None'
]**
不要在
cookieParams
中设置domain
参数。我已将其设置为我的域名,这会导致我的会话在页面重新加载后丢失。'session' => [ 'class' => 'yii\web\CacheSession', 'name' => 'naabnuts_session', 'timeout' => 30 * 24 * 3600, 'useCookies' => true, 'cookieParams' => [ 'httponly' => true, 'secure' => true, 'sameSite' => yii\web\Cookie::SAME_SITE_LAX, 'lifetime' => 30 * 24 * 3600, //'domain' => 'https://mydomain.com'=> 'will cause fault on phones' ] ]
似乎 Cookie 名称不能包含特殊字符,例如点(“.”)。
设置名称包含点的 Cookie 不会出现在 Yii::$app->request->cookies 集合中,因为名称中的点将转换为下划线,但序列化名称键仍然包含点。
参见 https://php.ac.cn/manual/de/function.setcookie.php#120794
注册 注册 或 登录 以发表评论。