You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel多租户集成WebSocket时Session Token频繁变更致自动登出问题

解决多租户与WebSocket共存时Session Token变更导致的登出问题

问题根源

Tenancy for Laravel包会基于租户标识(如域名、子域名或请求参数)隔离每个租户的Session存储。当WebSocket连接建立时,如果握手阶段未正确传递租户上下文信息,服务器端会默认以「非租户」上下文处理请求,此时会创建或使用不属于当前租户的Session,覆盖原有租户Session的Token,最终导致页面刷新后Session不匹配而登出。

具体解决方案

1. 在WebSocket握手时传递租户标识

前端初始化Socket.io连接时,通过查询参数传递当前租户的标识(比如租户ID、slug或子域名):

// 前端Socket.io初始化代码
const socket = io({
  query: {
    tenant_id: '{{ auth()->user()->tenant->id }}' // 替换为当前租户的有效标识
  },
  transports: ['websocket'], // 强制使用WebSocket传输,避免轮询丢失上下文
  credentials: true // 允许传递Session Cookie
});

2. 在WebSocket服务器中校验并绑定租户上下文

修改你的socket-io-server.js,在连接建立前校验租户标识,并确保后续请求基于该租户上下文处理:

const { Server } = require('socket.io');
const axios = require('axios');

const io = new Server(3000, {
  cors: {
    origin: "http://your-app-domain.com", // 替换为你的应用域名
    credentials: true
  }
});

io.use(async (socket, next) => {
  const tenantId = socket.handshake.query.tenant_id;
  if (!tenantId) {
    return next(new Error('租户标识缺失'));
  }

  // 调用Laravel后端接口校验租户有效性并确认Session归属
  try {
    const response = await axios.get(`http://your-app-domain.com/api/tenants/${tenantId}/validate`, {
      headers: {
        Cookie: socket.handshake.headers.cookie // 传递前端的Session Cookie
      }
    });

    if (response.data.valid) {
      socket.tenantId = tenantId;
      next();
    } else {
      next(new Error('无效的租户标识'));
    }
  } catch (err) {
    next(new Error('租户验证失败'));
  }
});

io.on('connection', (socket) => {
  console.log(`租户 ${socket.tenantId} 的客户端已连接`);
  // 后续事件处理均基于该租户上下文
});

3. 配置CORS确保Session Cookie正确传递

在Laravel的config/cors.php中,允许WebSocket服务器的域名并开启凭证支持:

// config/cors.php
return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => ['http://your-websocket-domain.com'], // 替换为WebSocket服务器域名
    'allowed_headers' => ['*'],
    'supports_credentials' => true,
];

4. 让WebSocket域名与租户子域名匹配(可选)

如果使用子域名区分租户,将WebSocket服务器的域名设置为对应租户的子域名(如tenant1.your-app.com:3000),这样Tenancy中间件会自动识别租户,Session会直接关联到租户的存储池,避免上下文混乱。

5. 禁用WebSocket请求的Session自动启动(可选)

如果WebSocket请求不需要使用Session,可在Laravel的StartSession中间件中添加判断,跳过WebSocket握手请求的Session初始化:

// App\Http\Middleware\StartSession
public function handle($request, Closure $next)
{
    // 检测是否为WebSocket握手请求
    if ($request->header('Upgrade') === 'websocket') {
        return $next($request);
    }
    return parent::handle($request, $next);
}

验证方法

修改配置后重启WebSocket服务器和Laravel应用,登录后建立WebSocket连接,刷新页面并检查$request->session()->token()的值是否保持稳定。若值未发生变更,说明问题已解决。

内容的提问来源于stack exchange,提问作者Virb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 01:10:25