如何在PHP中生成唯一的会话ID

7
在我们的网站上,我们希望能够实现多个域名间共享会话。这些网站都在同一台服务器上,但其中一些具有不同的IP地址。
我找到的可能解决方案是手动设置会话ID:
<?php
session_id($someUniqueHash);
?>

如果我像md5('test')那样创建哈希,则可以正常工作。在服务器上的另一个域中,我们再次拥有会话。

问题在于生成ID。我在互联网上看到了一些解决方案,例如使用微秒等,但是当我采用该方法时,无法预测其他域/ PHP页面上的会话ID。

有人有想法吗?或者我们不应该实施这个功能?是否有其他选项可以在多个域之间共享会话?(不是子域!)


1
为什么不让PHP为您生成会话ID呢?顺便说一下,您可以跨域共享会话ID。这要么是cookie的问题,要么是传递查询参数的问题。 - hakre
查询参数不被推荐使用,因为它不安全且不符合 URL 规范。据我所知,您只允许在同一域名下(包括子域名)使用 cookie,而不是多个域名之间。 - Kees Schepers
这只是一个你传递的 cookie。而且你可以为第三方域创建 cookie,所谓的第三方 cookie。你可能需要检查一下法律方面是否允许这样做(除了已解决的技术方面)。顺便说一句,你可能想学习所有技术细节:HTTP 状态管理机制 RFC6265 - hakre
谢谢,Hakre。我还不知道这个。我会调查你的选项并在这里回复。 - Kees Schepers
5个回答

5

我使用了OAuth类型的流程来实现这个系统,但我们用用户替换了消费者。

因此,每个域都会在自己的会话中拥有已认证的Access_Token。然后,您可以使用该Access_Token从api获取有关用户的信息。

我还使用session_set_save_handler解决了会话问题,并将会话存储在数据库表中......该表也会有Access_Token,使得通过DB查询轻松查找会话。

希望这能帮助您思考。


我最近一直在思考这个问题。不知道是否有关于将访问令牌添加到数据库表中并在每次请求时查询以提高存储和检索性能的案例研究或简单讨论。 - Tom Pace

0

单独配置每个站点:

<?php

$cfgsession['file'] = "../sessions_global.txt";
$cfgsession['keepalive'] = 7200;

?>

为了使多个站点共享会话,请让它们使用相同的$cfgsession['file']。 在向另一个域发出请求时,包含来自一个站点的会话(可能是由Jack推荐的),只要您不抓住他们在其他浏览器或其他地方进行请求(请采取措施防止会话劫持),就可以让他们使用$_GET指定会话。例如:
include ("../session.php");
if (isset($_COOKIE['session'])) session_begin($_COOKIE['session'], $_SERVER['HTTP_USER_AGENT'] . "+" . $_SERVER['HTTP_ACCEPT_CHARSET'], $_SERVER['REMOTE_ADDR']);
else session_begin("", $_SERVER['HTTP_USER_AGENT'] . "+" . $_SERVER['HTTP_ACCEPT_CHARSET'], $_SERVER['REMOTE_ADDR']);
setcookie("session", session_identity(), 0);

然后只需编写自己的session_函数:

<?php

function session_begin($mysession = "", $key = "", $client = "") {
  global $cfgsession;
  if (!preg_match("/^[a-z0-9]{32}$/i", $mysession)) $mysession = md5(microtime());
  $error = false;
  $client = trim($client);
  $key = trim($key);
  $cfgsession['returning'] = false;
  if ($chandle = @tmpfile()) {
    if ($shandle = @fopen($cfgsession['file'], "rb")) {
      flock($shandle, LOCK_SH);
      fputs($chandle, $mysession . " " . time() . " $" . $client . " $" . $key . "\n");
      while (!feof($shandle)) {
        $sline = explode(" ", trim(fgets($shandle)), 4);
        if ($sline[1] >= (time() - $cfgsession['keepalive'])) {
          if (($sline[0] == $mysession) && ($sline[3] == "$" . $key)) {
            $cfgsession['client'] = substr($sline[2], 1);
            $cfgsession['returning'] = true;
          } elseif (count($sline) > 2) fputs($chandle, implode(" ", $sline) . "\n");
        }
      }
      fclose($shandle);
      fseek($chandle, 0);
      if ($shandle = @fopen($cfgsession['file'], "cb")) {
        if (flock($shandle, LOCK_EX)) {
          ftruncate($shandle, 0);
          $cfgsession['count'] = 0;
          while (!feof($chandle)) {
            $cline = trim(fgets($chandle));
            fputs($shandle, $cline . "\n");
            $cfgsession['count']++;
          }
        } else $error = true;
        fclose($shandle);
      } else $error = true;
    } else $error = true;
    fclose($chandle);
  } else $error = true;
  if (($cfgsession['returning'] == false) && ($mysession == $cfgsession['session'])) {
    $cfgsession['returning'] = true;
    $mysession = md5(microtime());
  }
  $cfgsession['session'] = $mysession;

  if ($error) return -1;
  else return 0;
}

function session_count() {
  global $cfgsession;
  return $cfgsession['count'];
}

function session_client() {
  global $cfgsession;
  return $cfgsession['client'];
}

function session_id() {
  global $cfgsession;
  return $cfgsession['session'];
}

function session_index() {
  global $cfgsession;
  $index_return = array();
  if ($uhandle = @fopen($cfgsession['file'], "rb")) {
    flock($uhandle, LOCK_SH);
    while (!feof($uhandle)) {
      $uline = explode(" ", trim(fgets($uhandle)), 4);
      foreach ($uline as &$value) {
        if ($value[0] == "$") $value = substr($value, 1);
      }
      if (count($uline) >= 2) $index_return[] = $uline;
    }
    fclose($uhandle);
  }
  return $index_return;
}

function session_returning() {
  global $cfgsession;
  return $cfgsession['returning'];
}

?>

0
如果这些是登录会话,请考虑使用单点登录(SSO)解决方案,例如实现SAML标准的解决方案。

0

嗯,这是一个困难的问题。

众所周知,PHP使用cookie来理解当用户返回您的站点时的session_id,而且没有跨域cookie的方法:跨域cookie(编辑:有,但方法很复杂)。

这可能就是为什么我从未见过一个站点实现这个功能,即使它们有不同的域名。

您可以通过页面上的链接从一个域传递会话ID到下一个域,通过$_GET或$_POST。如果用户直接进入您的其他站点,则无法使用此方法。

我能想到的唯一部分(不可靠)的方法是在数据库中记录用户计算机,并使用它来了解附加到其上的会话。因此,您可以存储计算机的IP地址和其他一些详细信息,并将其与会话相关联。

人们计算机的IP和其他详细信息将使他们登录到其他域。


谢谢。有一个网站可以做到这一点:例如http://www.laptopshop.nl。我知道他们的网站是用PHP开发的,所以这是可能的。但我担心这很难。 - Kees Schepers
我纠正一下我的答案,跨域cookie是可能的,但像那篇文章中提到的那样可靠性很高,但是跨域会话确实不太可靠... - Sammaye

0

也许这对你来说不是一个选项,但你可以尝试一下。

在你的主站点上,按照正常方式生成会话 ID,并在 URL 中包含带有会话 ID 的图像标签,以延续会话到另一个域。作为响应,另一个域将设置一个 cookie,因此当访问者到达那里时,它已经知道会话 ID。

感觉有点聪明,但如果你没有太多其他域的话,它应该能够工作 :) 顺便提一下,第三方 cookie 可以在浏览器中单独禁用,这是需要考虑的事情。

顺便说一下,会话采用(通过查询参数接受 ID 并设置 cookie)是非常敏感的事情,必须受到保护,即在设置 cookie 之前必须已经存在会话。


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