簡體   English   中英

如何將 $_SESSION 變量傳遞給 websocket 服務器?

[英]How to pass $_SESSION variables to a websocket server?

我在 web 上搜索了很多,但沒有找到有用的線索。

我有一個 websocket 服務器和一個 web 服務器在我的本地機器上一起運行。

當客戶端使用瀏覽器 API 'new WebSocket("ws://localhost")' 連接到它時,我需要將 $_SESSION 數據傳遞給 websocket 服務器(請求使用知道它的反向代理發送到 websocket當收到帶有“升級”標頭的請求時)。

關鍵是客戶端成功連接到 ws 服務器,但我還需要使用 HTTP web 服務器設置的 $_SESSION 變量來恢復它們的 SESSION 數據。

實際上我的情況是這樣的(我使用的是 Ratchet 庫):

use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\MyAppClassChat;

require dirname(__DIR__) . '/vendor/autoload.php';

$server = IoServer::factory(new HttpServer(new WsServer(new MyAppClass())), 8080);
$server->run();

MyAppClass 非常簡單:

 <?php
namespace MyAppClass;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;

class MyAppClass implements MessageComponentInterface {

    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    public function onOpen(ConnectionInterface $conn) {
            /* I would like to put recover the session infos of the clients here
               but the session_start() call returns an empty array ($_SESSION variables have been previuosly set by the web server)*/
        session_start();
        var_dump($_SESSION) // empty array...
        echo "New connection! ({$conn->resourceId})\n";
    }

    public function onMessage(ConnectionInterface $from, $msg) {
        $numberOfReceivers = count($this->clients) -1;
        echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n", $from->resourceId, $msg, 
                                 $numberOfReceivers, $numberOfReceivers == 1 ? '' : 's');

        $this->clients->rewind();
        while ($this->clients->valid())
        {
            $client = $this->clients->current();
            if ($client !== $from) {
                $client->send($msg);
            }
            $this->clients->next();
        }
    }

    public function onClose(ConnectionInterface $conn) {
        $this->clients->detach($conn);
        echo "Connection {$conn->resourceId} has disconnected\n";
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        echo "An error has occurred: {$e->getMessage()}\n";
        $conn->close();
    }
}

有沒有辦法用我的實際布局來做到這一點,或者我應該配置 apache 以使用 mod_proxy_wstunnel 模塊?

感謝幫助!!!

正如其他 StackOverflow 答案所顯示的( 沒有 Symfony 會話的 Ratchet在棘輪 websocket 連接中啟動會話),無法在 Apache 和 Ratchet 進程之間直接共享 $_SESSION 變量。 但是,可以啟動與 Apache 服務器的會話,然后在 Ratchet 代碼中訪問會話 cookie。

Apache 服務器的 index.html 啟動會話:

<?php
// Get the session ID.
$ses_id = session_id();
if (empty($ses_id)) {
    session_start();
    $ses_id = session_id();
}
?><!DOCTYPE html> ...

Ratchet MessageComponentInterface 代碼訪問會話令牌:

public function onMessage(ConnectionInterface $from, $msg) {
    $sessionId = $from->WebSocket->request->getCookies()['PHPSESSID'];
    # Do stuff with the token...
}

一旦兩台服務器都知道用戶的會話令牌,它們就可以使用令牌通過 MySQL 數據庫共享信息(這就是我所做的):

    # Access session data from a database:
    $stmt = $this->mysqli->prepare("SELECT * FROM users WHERE cookie=?");
    $stmt->bind_param('s', $sessionId);
    $stmt->execute();
    $result = $stmt->get_result();

或者,您可以進行更奇特的進程間通信形式:

    # Ratchet server:
    $opts = array(
        'http'=>array(
            'method'=>'GET',
            'header'=>"Cookie: PHPSESSID=$sessionId\r\n"
        )
    );
    $context = stream_context_create($opts);
    $json = file_get_contents('http://localhost:80/get_session_info.php', false, $context);
    $session_data = json_decode($json);

    # Apache server's get_session_info.php
    # Note: restrict access to this path so that remote users can't dump
    # their own session data.
    echo json_encode($_SESSION);

這可能看起來有點笨拙,但這是我想出如何實現這一點的唯一方法。

假設您已經知道目錄和 sessionId,您可以使用session_encode()session_decode()直接從會話文件中讀取數據,如下所示。 我的會話文件以sess_為前綴,其他人可能不是這樣,所以請記住這一點。 請注意,這將在從會話文件中提取變量后保存並恢復任何現有的$_SESSION數據:

$contents = file_get_contents($sessionDir . 'sess_' . $sessionId);
$sessionData = $this->decodeSession($contents);
return $sessionData;

private function decodeSession($sessionString)
{
   $currentSession = session_encode();
   foreach ($_SESSION as $key => $value){
     unset($_SESSION[$key]);
   }
   session_decode($sessionString);
   $sessionVariables = $_SESSION;
   foreach ($_SESSION as $key => $value){
     unset($_SESSION[$key]);
   }
   session_decode($currentSession);
   return $sessionVariables;
}

為了完成dmiller309和fred的回答,我還不能評論:(

我不會將 session 存儲在數據庫中,我只會使用保存會話的目錄,使用session_save_path()搜索我感興趣的目錄。

public function onMessage(ConnectionInterface $from, $msg) {
    $session_file = session_save_path()."/sess_".$from->WebSocket->request->getCookies()['PHPSESSID'];
    if(!file_exists($session_file))
        // The session doesn't exist
        return ;
    $content = file_get_contents($session_file);
    $session_id = "0";
    foreach($sections = explode(";", $content) as $k => $section) {
        $data = explode("|", $section, 2);
        if(isset($data[0]) and $data[0] == "id" and isset($data[1]) and substr_count($data[1], '"') == 2) {
            $session_id = substr($data[1], strpos($data[1], '"')+1, -1);
            break;
        }
    }
    if($session_id == "0")
        // The session has no "id"
        return ;

    // ID stored in $session_id, equivalent to $_SESSION["id"]
}

注意:所有會話都以“sess_”開頭

注意 2:我將 session ID 存儲在一個字符串中,所以我在引號之間獲取它。

注意 3:出於安全原因,我沒有使用session_decode() ,因為結果存儲在$_SESSION中,我認為它可能會導致Race Condition

我沒有測試,如果有什么不對,你可以告訴我。

當然,可以對存儲在 session 中的其他值執行相同的操作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM