跨子域的PHP会话管理

97

我正在尝试进行以下设置:

auth.example.com
sub1.example.com
sub2.example.com
如果用户访问sub1.example.comsub2.example.com且未登录,则会被重定向到auth.example.com并可以登录。 sub1.example.comsub2.example.com是两个独立的应用程序,但使用相同的凭据。
我尝试在我的php.ini中设置以下内容:
session.cookie_domain = ".example.com"

但是似乎没有将信息从一个域传递到另一个域。

[编辑]

我尝试了以下内容:

sub1.example.com/test.php

session_set_cookie_params(0, '/', '.example.com');
session_start();
print session_id() . "<br>";
$_SESSION['Regsitered'] = 1;
echo '<a href="http://auth.example.com/test.php">Change Sites</a>'

auth.example.com/test.php

session_set_cookie_params(0, '/', '.example.com');
session_start();
print session_id() . "<br>";
$_SESSION['Checked'] = 1;
print_r($_SESSION);

会话 ID 是完全相同的,但是当我转储 $_SESSION 变量时,并未显示两个键,只显示我在每个域下设置的任何键。


你还需要在代码中启用它,参见http://us2.php.net/manual/en/function.session-set-cookie-params.php。 - Residuum
1
我几乎有相同的设置(我使用“session_set_cookie_params”调用来设置会话cookie域),它可以正常工作。 - Milen A. Radev
这里有一个很好的函数可以工作。http://stackoverflow.com/questions/2835486/php-session-shared-with-subdomain/17638102#17638102 - boksiora
16个回答

145

我不知道这个问题是否仍然存在,但是我刚遇到了同样的问题,并通过在调用session_set_cookie_params()之前设置会话名称来解决它:

$some_name = session_name("some_name");
session_set_cookie_params(0, '/', '.example.com');
session_start();

我在php.ini中没有做任何更改,但现在一切都正常工作了。


10
我确认,它解决了问题。我试图在这里找到答案:http://stackoverflow.com/questions/4948340/how-to-pass-session-variable-to-a-page-in-the-parent-directory。但我在这里找到了它。 - Roman
5
完美!我找了很久才找到这个。是$some_name = session_name("some_name");解决了问题。谢谢并点赞。 - Kit
4
session_name("domain"); 对我来说也是必不可少的遗漏。 php.net 的文档在有关这些会话设置方面缺乏说明。php.net 上的社区帖子表明,在应用对 session_set_cookie_params() 的更改之前,需要定义 session.name。 - David Carroll
3
是的,确认了。不错啊,之前一直兜圈子了 ;) - Daithí
1
它对我也起作用了,解决了问题,感觉像奇迹一样。 - Homnath Bagale
显示剩余9条评论

24

有一件事情可能会神秘地阻止子域名上读取会话数据,尽管cookie已经正确设置为.example.com,这就是PHP Suhosin补丁。你可以按照问题中的示例正确地配置所有内容,但它可能不起作用。

关闭以下Suhosin会话设置,问题就解决了:

suhosin.session.cryptua = Off 
suhosin.session.cryptdocroot = Off

6

尝试使用以下方法:

session.cookie_domain = "example.com"

改为:

session.cookie_domain = ".example.com"

请注意开头没有句号。

9
不支持哪些浏览器? - gawpertron
11
浏览器支持与此有何关系?这是服务器端的操作。 - Kuf
对于未来的读者,请确保在此之后重启您的 PHP,而不仅仅是 Apache/Nginx。 - Beyondo

4
我遇到了这个问题 - 我想让在x.example.local创建的会话值在example.local上可用,反之亦然。
我发现的所有解决方案都建议通过在.htaccess(或通过php.ini或ini_set)中使用php_value session.cookie_domain .example.local来更改Session域。
问题是,我为所有子域设置了session.cookie_domain(到目前为止还好),但也为主域设置了session.cookie_domain。显然,在主域上设置session.cookie_domain是不行的。
基本上对我有用的方式是:
  • 为所有子域设置session.cookie_domain
  • 不要为主域设置它
哦,对了,请确保域具有TLD(在我的情况下为.local)。Http协议不允许在没有.tld的域上存储cookie /会话(即localhost不起作用,但stuff.localhost会起作用)。 编辑:同时请确保在测试/调试跨子域会话时始终清除浏览器cookie。如果不这样做,您的浏览器将始终发送旧的会话cookie,该cookie可能尚未正确设置cookie_domain。服务器将恢复旧会话,因此您将获得错误的负面结果。(在许多帖子中,提到使用session_name('stuff')具有完全相同的效果)

3
我已确认。joreon的回答是正确的。因为我的声誉不够,所以我无法发表评论,只能在这里发表评论。
在配置文件中定义常量。如果你想改变它,无需修改整个文件。
define('ROOT_DOMAIN',   'mysite.example');
define('PHP_SESSION_NAME', 'MYSITE'); 

会话名称不能仅由数字组成,必须至少包含一个字母。否则,每次都会生成新的会话ID。

使用以下代码开始使用会话:

session_name(PHP_SESSION_NAME);
session_set_cookie_params(0, '/', '.' . ROOT_DOMAIN);
session_start();

我正在使用这个函数:

function load_session() {
    if (session_status() == PHP_SESSION_NONE) {
        session_name(PHP_SESSION_NAME);
        session_set_cookie_params(0, '/', '.' . ROOT_DOMAIN);
        session_start();
    } elseif (session_name() != PHP_SESSION_NAME) {
        session_destroy();
        session_name(PHP_SESSION_NAME);
        session_set_cookie_params(0, '/', '.' . ROOT_DOMAIN);
        session_start();
    }
}
load_session(); // put it in anywhere you want to use session

3
我是这样解决的。
ini_set('session.cookie_domain', '.testdomain.example');
session_start();

因为我在本地主机上工作

ini_set('session.cookie_domain', '.localhost');

“ wasn't working ”的意思是“没有工作”,它看到 '.localhost' 作为顶级域名而不是 '.com/.local/...'(我怀疑)。

也为我的机器解决了这个问题 - Ubuntu 14.04 - dennis

1

使用这个,它有效:

ini_set('session.cookie_domain', 
    substr($_SERVER['SERVER_NAME'],strpos($_SERVER['SERVER_NAME'],"."),100));

这看起来像是为tld设置cookie...或者我漏掉了什么吗? - chacham15

1
子域名和根域名Cookie会话的组合使用
资源:http://php.net//manual/tr/function.session-set-cookie-params.php 我已经测试过了。
sub.example.com/sessionadd.php?id=123

example.com/sessionview.php // 123

-- 代码
<?php 
$currentCookieParams = session_get_cookie_params(); 

$rootDomain = '.example.com'; 

session_set_cookie_params( 
    $currentCookieParams["lifetime"], 
    $currentCookieParams["path"], 
    $rootDomain, 
    $currentCookieParams["secure"], 
    $currentCookieParams["httponly"] 
); 

session_name('mysessionname'); 
session_start(); 

setcookie($cookieName, $cookieValue, time() + 3600, '/', $rootDomain); 
?>

0

我知道这已经有点老了,但是对于我在同一台服务器上使用多个域名和子域名来说,这仍然很有效。

<?php
define('site_domain','example.com');
session_set_save_handler('_open',
                         '_close',
                         '_read',
                         '_write',
                         '_destroy',
                         '_clean');

function _open(){

    global $_sess_db;

$db_user = 'user';
$db_pass = 'pass';
$db_host = 'localhost';

if ($_sess_db = mysql_connect($db_host, $db_user, $db_pass)){

    return mysql_select_db('database', $_sess_db);

}

return false;

}

function _close(){

    global $_sess_db;
    return mysql_close($_sess_db);

}

function _read($id){

    global $_sess_db;
    $id = mysql_real_escape_string($id);
    $domain = mysql_real_escape_string(site_domain);
    $agent = mysql_real_escape_string(isset($_SERVER['HTTP_USER_AGENT']));

    $sql = "SELECT data
    FROM sessions
    WHERE id = '$id' AND domain = '$domain' AND agent = '$agent'";

     if ($result = mysql_query($sql, $_sess_db)){

         if (mysql_num_rows($result)){
             $record = mysql_fetch_assoc($result);
             return $record['data'];
        }

    }

    return '';

}

function _write($id, $data){

    global $_sess_db;
    $access = time();

    $id = mysql_real_escape_string($id);
    $access = mysql_real_escape_string($access);
    $data = mysql_real_escape_string($data);
    $domain = mysql_real_escape_string(site_domain);
    $agent = mysql_real_escape_string(isset($_SERVER['HTTP_USER_AGENT']));

    $sql = "REPLACE INTO sessions
    VALUES ('$id', '$access', '$data', '$domain', '$agent')";

    return mysql_query($sql, $_sess_db);

}

function _destroy($id){

    global $_sess_db;
    $id = mysql_real_escape_string($id);
    $domain = mysql_real_escape_string(site_domain);
    $agent = mysql_real_escape_string(isset($_SERVER['HTTP_USER_AGENT']));

    $sql = "DELETE FROM sessions
    WHERE id = '$id' AND domain = '$domain' AND agent = '$agent'";

    return mysql_query($sql, $_sess_db);

}

function _clean($max){

    global $_sess_db;
    $old = time() - $max;
    $old = mysql_real_escape_string($old);
    $domain = mysql_real_escape_string(site_domain);
    $agent = mysql_real_escape_string(isset($_SERVER['HTTP_USER_AGENT']));

    $sql = "DELETE FROM sessions
    WHERE  access < '$old' AND domain = '$domain' AND agent = '$agent'";

    return mysql_query($sql, $_sess_db);

}

?>


6
你需要回答什么问题?这个回答如何改进/增强其他9个回答的内容? - random_user_name

0

只需在 session_start() 方法的上方尝试使用以下代码

$sess_life_time = 21600; //in seconds
$sess_path = "/";
$sess_domain = ".example.com";
$sess_secure = true; // if you have secured session
$sess_httponly = true; // httponly flag

session_set_cookie_params($sess_life_time, $sess_path, $sess_domain, $sess_secure, $sess_httponly);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接