如何在CodeIgniter与Ratchet PHP WebSocket间共享会话变量以识别连接用户?
当然可以实现!要在CodeIgniter和Ratchet WebSocket之间共享会话、识别连接用户,核心是让两者能访问同一份会话数据,同时避开异步环境下PHP原生会话的锁阻塞问题。下面是几种经过验证的方案:
方案1:利用CodeIgniter的数据库会话存储
如果你的CodeIgniter已经配置了数据库存储会话(这是最常见的场景),可以让Ratchet直接查询数据库中的会话记录来获取用户身份:
步骤1:确认CodeIgniter的会话配置
先确保CodeIgniter使用数据库会话驱动,在application/config/config.php中设置:
$config['sess_driver'] = 'database'; $config['sess_save_path'] = 'ci_sessions'; // 你的会话表名 $config['sess_encrypt_cookie'] = true; // 如果开启了加密,后续要对应处理 $config['encryption_key'] = 'your_encryption_key_here'; // 你的加密密钥
会话表的结构可以参考CodeIgniter官方文档生成。
步骤2:在Ratchet中解析并验证会话
在你的Ratchet组件类中,解析客户端Cookie中的CodeIgniter会话ID,然后查询数据库解密会话数据:
use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; use PDO; class UserAwareWebSocket implements MessageComponentInterface { protected $clients; protected $db; private $encryptionKey; public function __construct() { $this->clients = new \SplObjectStorage; // 连接到CodeIgniter使用的同一个数据库 $this->db = new PDO('mysql:host=localhost;dbname=your_ci_db', 'db_user', 'db_pass'); // 复制CodeIgniter的加密密钥 $this->encryptionKey = 'your_encryption_key_here'; } public function onOpen(ConnectionInterface $conn) { // 从请求Cookie中获取CodeIgniter会话ID $cookies = $conn->WebSocket->request->getCookies(); if (!isset($cookies['ci_session'])) { $conn->close(); // 无会话,拒绝连接 return; } $sessionId = $cookies['ci_session']; // 查询有效会话(未过期) $stmt = $this->db->prepare("SELECT data FROM ci_sessions WHERE id = ? AND expires > UNIX_TIMESTAMP()"); $stmt->execute([$sessionId]); $encryptedSession = $stmt->fetchColumn(); if (!$encryptedSession) { $conn->close(); // 会话过期或不存在,拒绝连接 return; } // 解密会话数据(和CodeIgniter的加密逻辑一致) $decoded = base64_decode($encryptedSession); $ivLength = openssl_cipher_iv_length('AES-256-CBC'); $iv = substr($decoded, 0, $ivLength); $encryptedData = substr($decoded, $ivLength); $decryptedData = openssl_decrypt($encryptedData, 'AES-256-CBC', $this->encryptionKey, OPENSSL_RAW_DATA, $iv); $session = unserialize($decryptedData); // 获取用户ID(假设你的会话中存储了user_id字段) if (isset($session['user_id'])) { $conn->user_id = $session['user_id']; echo "用户 {$session['user_id']} 已连接\n"; $this->clients->attach($conn); } else { $conn->close(); // 未登录用户,拒绝连接 } } // 实现onMessage、onClose、onError方法... public function onMessage(ConnectionInterface $from, $msg) { // 可以使用$from->user_id识别发送消息的用户 echo "用户 {$from->user_id} 发送消息:{$msg}\n"; } public function onClose(ConnectionInterface $conn) { $this->clients->detach($conn); if (isset($conn->user_id)) { echo "用户 {$conn->user_id} 已断开连接\n"; } } public function onError(ConnectionInterface $conn, \Exception $e) { $conn->close(); } }
方案2:使用Redis作为共享会话存储
如果你的项目已经在使用Redis,这是更优的方案——Redis天生适合异步环境,无锁且性能高:
步骤1:配置CodeIgniter使用Redis会话
在application/config/config.php中设置:
$config['sess_driver'] = 'redis'; $config['sess_save_path'] = 'tcp://localhost:6379'; // 你的Redis地址 $config['sess_encrypt_cookie'] = false; // 可选,若开启加密仍需解密
步骤2:在Ratchet中连接Redis获取会话
使用predis/predis库连接Redis,直接读取会话数据:
use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; use Predis\Client; class RedisWebSocket implements MessageComponentInterface { protected $clients; protected $redis; public function __construct() { $this->clients = new \SplObjectStorage; // 连接到同一个Redis实例 $this->redis = new Client('tcp://localhost:6379'); } public function onOpen(ConnectionInterface $conn) { $cookies = $conn->WebSocket->request->getCookies(); if (!isset($cookies['ci_session'])) { $conn->close(); return; } $sessionId = $cookies['ci_session']; // 从Redis中获取会话数据(CodeIgniter的Redis会话键格式为:ci_session:<session_id>) $sessionData = $this->redis->get("ci_session:{$sessionId}"); if (!$sessionData) { $conn->close(); return; } // 反序列化会话数据(若未加密) $session = unserialize($sessionData); if (isset($session['user_id'])) { $conn->user_id = $session['user_id']; $this->clients->attach($conn); echo "用户 {$session['user_id']} 已连接\n"; } else { $conn->close(); } } // 其他方法实现... }
关键注意事项
- Cookie传递:前端连接WebSocket时,必须确保Cookie被发送。如果是浏览器环境,要设置
WebSocket实例的withCredentials: true,同时注意跨域场景下的CookieSameSite和Secure属性配置。 - 会话一致性:确保Ratchet中的加密密钥、会话存储配置和CodeIgniter完全一致,否则会出现解密失败或会话无法读取的问题。
- 异步环境限制:绝对不要在Ratchet代码中使用
session_start(),因为PHP原生会话是阻塞式的,会导致WebSocket服务卡死。 - 会话过期处理:查询会话时一定要检查过期时间,避免使用已失效的会话数据。
内容的提问来源于stack exchange,提问作者authentic972
相关产品推荐
相关产品推荐

