繁体   English   中英

session.cookie_domain在2个子域中的Plesk / PHP应用程序中无法正常工作

[英]session.cookie_domain not working as expected in Plesk / PHP application on 2 subdomains

编辑29/1/2019 - 这个问题有资格获得赏金,但没有一个提供的答案直接解决问题。 如果您的答案基于所询问的内容而有效,请回复

我在同一物理服务器上配置了2个域:

app.example.com
help.app.example.com

用户登录到https://app.example.com/ ,它由PHP 5.5应用程序组成,该应用程序在成功登录后将一些数据存储在$_SESSION数组中。

我想配置https://help.app.example.com/以便我可以阅读https://app.example.com/上的会话数据。 help子域中的应用程序是一个用PHP 7构建的内容管理系统。

Plesk中我已经添加以下为“附加配置指令”的php.ini两个 app.example.comhelp.app.example.com

session.cookie_domain = ".example.com"

如果我将一个phpinfo()脚本上传到help.app.example.com它会显示以下session.cookie_domain

  • 本地价值:没有价值
  • 主值: .example.com

如果我然后在help.app.example.com上的脚本中运行以下help.app.example.com

<?php
session_start();
var_dump($_SESSION);
die;
?>

它输出一个空数组:

array(0) { }

但是,如果我在app.example.com上运行等效app.example.com ,则会输出一个会话数据数组,其中显示了已登录用户的详细信息(如预期的那样):

array(15) {
   ["o_id"]=> (1) "1"
   ["u_id"]=> string(4) "1745"
   ...
}

我期待在两个子域上看到相同的输出。 为什么这不起作用?

我已阅读允许php会话转移到子域,但没有一个解决问题。

为什么这不起作用?

默认情况下会话保存到服务器上的本地文件,其位置在php.ini的session.save_path指定,例如session.save_path = /var/lib/php/sessions ,如果是app.example.com和help.app .example.com运行在具有自己的文件系统的2个不同服务器上,或者即使它在同一文件系统上运行但在php.ini中具有不同的session.save_path指令,它们也不会共享相同的$ _SESSION。

如果您希望2个不同的服务器共享相同的$ _SESSION,可能的解决方案包括使用session_set_save_handler()创建共享会话存储数据库(如想到MongoDB或MySQL),或创建网络文件系统并设置session.save_path = /path/to/networked/filesystem/mountpoint php.ini中的session.save_path = /path/to/networked/filesystem/mountpoint ,但这两种方法都可能导致显着的性能损失。

...由于cookie在两个域之间共享, session_id()将在两端返回相同的值,可以用作会话数据库的id,请查看http://php.net/manual/ EN / class.sessionhandlerinterface.php

(如果我有更多时间,我会写一个样本课,但我没时间)

切换到sql-db支持的会话存储(如MariaDB,MySQL或PostgreSQL),例如:schema:

CREATE TABLE sessions (
  id VARCHAR(255) ,
  atime BIGINT ,
  data BLOB
)

SessionHandlerInterface实现:

class MySqlSessionHandler implements SessionHandlerInterface
{
    protected $db;
    public function __construct(string $dsn, string $username, string $password)
    {
        $this->db = new PDO($dsn, $username, $password, array(
            PDO::ATTR_EMULATE_PREPARES => false,
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        ));
    }
    protected function a(string $id) : bool
    {
        $ret = $this->db->query("UPDATE sessions SET atime = " . (time()) . " WHERE id = " . $this->db->quote($id));
        return ($ret->rowCount() > 0);
    }
    public function close() : bool
    {
        // TODO: implement locking/race-condition-free session handling?
        return true;
    }
    public function destroy(string $id) : bool
    {
        $this->db->query("DELETE FROM sessions WHERE id = " . $db->quote($id));
        return true;
    }
    public function gc(int $maxlifetime) : int
    {
        $this->db->query("DELETE FROM sessions WHERE atime < " . (time() - $maxlifetime));
        return 1; // ??? not sure what this return int is supposed to contain, docs doesn't say either
    }
    public function open(string $save_path, string $session_name) : bool
    {
        if (!$this->a($session_name)) {
            $stm = $this->db->prepare("INSERT INTO sessions (id,atime,data) VALUES(?,?,?);");
            $stm->execute(array($session_name, time(), serialize(null)));
        }
        return true;
    }
    public function read(string $session_id) : string
    {
        if (!$this->a($session_id)) {
            throw new \InvalidArgumentException("supplied session id does not exist.");
        }
        return $this->db->query("SELECT data FROM sessions WHERE id = " . $this->db->quote($session_id))->fetch(PDO::FETCH_ASSOC)['data'];
    }
    public function write(string $session_id, string $session_data) : bool
    {
        // optimization note: this function can be optimized to do everything in a single query, instead of using a() (which also use a query)
        if (!$this->a($session_id)) {
            throw new \InvalidArgumentException("supplied session id does not exist.");
        }
        $stm = $this->db->prepare("UPDATE sessions SET data = ? WHERE id = ?");
        $stm->execute(array($session_data, $session_id));
        return true;
    }
}

用法:

// for DSN documentation, check http://php.net/manual/en/ref.pdo-mysql.connection.php
$handler = new MySqlSessionHandler ('mysql:host=mydb.foo.com;dbname=sessions;charset=utf8mb4','MySqlUsername','MySqlPassword');
session_set_save_handler($handler, true);
session_start();
  • 现在他们肯定应该分享会议..
  • 警告:在编写时未经测试,但这应该在理论上有效。

我只是尝试复制whit:

  • 产品:Plesk Onyx 17.8.11更新#37
  • PHP版本7.0.32-0ubuntu0.16.04.1

并且工作得很好。 在两种情况下,我都在_Session数组中获得了值,即使我只将值保存在子域中。

在我的集合中,php.ini的“附加配置指令”对于域和子域是相同的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM