【问题标题】:PHP - Session per TabPHP - 每个选项卡的会话
【发布时间】:2016-04-20 21:29:08
【问题描述】:

是否可以为每个浏览器选项卡创建会话?

例如,用户在其浏览器中打开了 2 个选项卡: 标签 1标签 2

Tab 1他有一个会话:

$_SESSION['xxx'] = 'lorem';

Tab 2 中,会话是:

$_SESSION['xxx'] = 'ipsum';

现在刷新时,我需要在活动选项卡中获取当前会话。例如,如果用户刷新选项卡 2,我需要在加载时获取选项卡 2 的$_SESSION['xxx'],即“ipsum”。但$_SESSION['xxx'] 不应在 Tab 1 上更改。

是否有任何选项可以保存每个选项卡的会话。如果不是,还有什么其他方法可以处理这个问题?

感谢您的帮助!

【问题讨论】:

  • 标签,你的意思是浏览器标签,对吧?
  • 这和这个问题非常相似:stackoverflow.com/questions/6067009/…你可以在那里找到你的答案。
  • 我遇到了这个问题,我正在使用像$_SESSION['xxx']['uniquevalue']这样的数组,但我很幸运我用它来处理提交表单,所以我可以添加一个具有唯一值的唯一隐藏字段......不知道我的方法是否可以接受,但它有效.. :)

标签: php session tabs


【解决方案1】:

PHP 将会话 ID 存储在 cookie 中,并且 cookie 是每个客户端(浏览器)的,而不是选项卡。所以没有简单易行的方法来做到这一点。有很多方法可以通过创建自己的会话处理程序来做到这一点,但它们比解决方案更多,因此也有其自身的风险和复杂性。无论出于何种原因,您可能需要这个,我很确定有比会话拆分更好的架构解决方案。

【讨论】:

  • 是的,我用 url 参数做到了 :) 感谢您的回答!
  • @Auris,当您打开多个窗口时,这是一个很好的用途。一个有新闻,另一个有 Facebook,第三个有你的 Magnificent 应用程序,需要登录。使用普通会话 cookie,您可以登录,然后关闭该选项卡/窗口。但由于其他选项卡/窗口仍处于打开状态,因此会话保持活动状态。您可以重新打开一个新选项卡/窗口,而无需登录。我认为如果应用程序窗口关闭时会话终止,从而强制重新登录会更好。使用普通会话 cookie,会话在所有窗口都关闭之前不会终止。
  • @UncaAlby 嗨,当您关闭窗口时,会话实际上会终止,如果 cookie 未过期,则一旦您再次打开它,它就会使用存储在 cookie 中的 id 重新构建。然而,这不是问题的重点。 Ihuber 希望在不同选项卡中为同一实体创建 2 个单独的会话。这是 clinet 功能,在服务器端没有“干净”的方法可以做到这一点。在您的示例中,您使用了 3 个不同的应用程序。这些应用程序有自己的会话实体,彼此无法访问(例如,您的超级应用程序无法管理 FB 会话容器中的变量 - 网络安全 101)。
【解决方案2】:

我一直在网上搜索这个问题的答案,但还没有找到令人满意的解决方案。我终于在 JavaScript 中整合了一些可行的东西。

//generate a random ID, doesn't really matter how    
if(!sessionStorage.tab) {
    var max = 99999999;
    var min = 10000000;
    sessionStorage.tab = Math.floor(Math.random() * (max - min + 1) + min);
}

//set tab_id cookie before leaving page
window.addEventListener('beforeunload', function() {
    document.cookie = 'tab_id=' + sessionStorage.tab;
});

HTML5 sessionStorage 不会在标签之间共享,因此我们可以在其中存储唯一的标签 ID。监听窗口上的beforeunload 事件告诉我们我们要离开(并加载其他页面)。通过在我们离开之前设置一个 cookie,我们将我们的值包含在新请求中,而无需任何额外的 URL 操作。要区分选项卡,您只需检查服务器上的 $_COOKIE['tab_id'] 并适当地存储会话值。

请注意 Firefox 的行为很奇怪,因为触发 window.open() 将创建一个与其父级共享 sessionStorage 的窗口,从而为您提供两个具有相同 ID 的选项卡。手动打开一个空白选项卡,然后导航到目标 URL 将为您提供单独的存储空间。到目前为止,Chrome 在我的所有测试中都适用于我。

我意识到这可能不是正确的答案,甚至不是一个“好”的答案,但它是一个的答案。

【讨论】:

  • 是的,这可能有效,但同样,这是一个 hack。如果您正在构建一些个人的东西并使用它,那很好,但是如果您正在构建一个合适的商业产品并且有人审查您的代码,那么您很可能会因此而被解雇。您将基于应用程序的会话存储在选项卡 ID 下,这意味着在公共计算机上,您的会话可供打开该选项卡 ID 的任何人使用。此外,如果用户以不同的 tab 顺序加载具有不同会话的应用程序组件怎么办? - 就像我说的,这很适合玩耍,但不能用于适当的解决方案。
  • 我认为你的批评有点不公平,因为这是一个正确解决方案的开始:不是将会话 ID 从 cookie 传递到网站,而是从网站的 cookie。
  • @Auris CSRF 令牌以几乎相同的方式隔离会话,它们几乎是每个主要框架的一部分。 CSRF 令牌是安全的,因为它仅用于将无效请求列入黑名单,它本身不会将请求列入白名单。同样,这种选项卡令牌只能有助于提高安全性,只要您以一种不会跳过所有其他正常会话处理的方式来处理它。
  • 我建议不要将 sessionStorage.tab 存储为随机数,而是将其设置为自动递增的数字。这将防止任何可能的碰撞导致随机错误。
【解决方案3】:

我一直在尝试制作一个具有此功能的网络应用程序。

它还没有成熟并且有限制和流程,例如如果您的 javascript 根据它向外部 php 代码发布帖子,则必须在 url 中传输会话 id,但它是功能性的并且适合我的需求(目前) .

我正在考虑一个更安全的解决方案,因此请随时根据您的需求调整它并给我您的建议。

<?php
/**
* Split $_SESSION by browser Tab emulator.
* methods exemples are used whith :
* $session = new SessionSplit();
* as SessionSplit may reload the page, it has to be used on top of the code.
* 
*/
class SessionSplit{
    public $id;
    private $gprefix="session_slice_";
    private $prefix="";
    private $witness="";
    function SessionSplit($witness='witness'){
        if(session_status()===PHP_SESSION_NONE){
            session_start();
        }
        $this->witness=$witness;
        if($this->get_id()){
            $this->prefix=$this->gprefix.$this->id;
            //set a witness to 'register' the session id
            $this->set($this->witness,'true');
        }else{
            // force the session id in the url to not interfere with form validation
            $actual_link = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
            $new_link = $actual_link.(strpos($actual_link,'?')===false?'?':'&').
                'session_id='.$this->id;
            header('Location: '.$new_link); 
        }
    }
    private function get_id(){
        if(isset($_GET['session_id'])){
            $this->id=$_GET['session_id'];
            return true;
        }else{
            $this->new_id();
            return false;
        }
    }
    private function new_id(){
        $id=0;
        while(isset($_SESSION[$this->gprefix.$id.'.'.$this->witness])){$id++;}
        $this->id=$id;
    }
    // ----------- publics
    public function clearAll(){
        foreach($_SESSION as $key=>$value){
            if(strpos($key,$this->prefix.'.')===0){
                unset($_SESSION[$key]);
            }
        }
    }
    /**
    * $is_user=$session->has('user');
    * equivalent to
    * $is_user=isset($_SESSION['user']);
    * @param {string} $local_id 
    * @return {boolean}
    */
    public function has($local_id){
        return isset($_SESSION[$this->prefix.'.'.$local_id]);
    }
    /**
    * 
    * $session->clear('user');
    * equivalent to
    * unset($_SESSION['user']);
    * @param {string} $local_id 
    */
    public function clear($local_id){
        unset($_SESSION[$this->prefix.'.'.$local_id]);
    }
    /**
    * $user=$session->get('user');
    * equivalent to
    * $user=$_SESSION['user'];
    * @param {string} $local_id 
    * @return {mixed}
    */
    public function get($local_id){
        if (isset($_SESSION[$this->prefix.'.'.$local_id])) {
            return $_SESSION[$this->prefix.'.'.$local_id];
        }else return null;
    }
    /**
    * $session->set('user',$user);
    * equivalent to
    * $_SESSION['user']=$user;
    * @param {string} $local_id 
    * @param {mixed} $value
    */
    public function set($local_id,$value){
        $_SESSION[$this->prefix.'.'.$local_id]=$value;
    }
};
?>

【讨论】:

    【解决方案4】:

    这是我的解决方案;我们使用它来允许每个客户端打开多个应用程序视图。

    POST 获取数据: 'doStuff=getData&model=GTD&sn=6789&type=random&date=18-Dec-2018'

    控制器然后从表中获取数据,为设备构建对象,将其存储到会话对象中,并将其存储在变量中。它会生成每个选项卡的 guid,以便用户可以在 UI 中将同一仪器与不同视图进行比较。

    $_session[$model][$sn][$type][$guid][$key];

    当然,guid 也会在数据对象中发回,以便选项卡知道以后如何调用该数据。

    当用户想要将结果打印到文件(pdf 等)时,它会在 POST 中发送包含相关数据的帖子。

    'doStuff=saveFile&model=GTD&sn=6789&type=random&calDate=18-Dec-2018&guid=randomKey'

    然后控制器会将其传递给存储进行检索。

    会话类文件示例:

    <?php
    class Session {
    public $fieldKey1;
    public $fieldKey2;
    
    public function GetStorage($model, $sn, $type, $guid, $key) {
        return $_SESSION[$model][$sn][$type][$guid][$key];
    }
    
    }
    
    ?>
    

    控制器文件:

    <?php
    require_once('session.php'); 
    global $session; //from session class file
    switch($_POST['doStuff']) {
      case 'saveFile':
         $session->GetStorage($_POST['model'], $_POST['sn'], $_POST['type'], $_POST['guid'], $key);
         break;
    }
    ?>
    

    这允许用户拥有相同数据的多个视图,而不会覆盖每个选项卡中的数据集。如果您不需要每个选项卡的数据粒度,您当然可以简化 $_SESSION 变量的键数。

    【讨论】:

    • 这个答案激励我创建另一个像这样的 php 会话 $_SESSION['xyz'] = "value1, value2"
    猜你喜欢
    • 1970-01-01
    • 2014-02-21
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多