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
相关产品推荐
相关产品推荐

