在 Laravel 12 中实现 WebSocket 通信时,若需在身份验证失败后主动断开客户端连接,需结合 频道认证机制 和 服务端主动断连操作。以下是具体实现步骤:
一、身份验证流程设计
WebSocket 连接的身份验证通常通过 私有频道(Private Channel) 或 存在频道(Presence Channel) 实现。以下是核心流程:
- 客户端发起订阅请求:
// 前端订阅私有频道 Echo.private('chat.1') .listen('NewMessage', (data) => { /* ... */ });
- 服务端验证权限:
- Laravel 自动触发
routes/channels.php
中的频道授权逻辑。 - 若验证失败,返回
403 Forbidden
,并触发断连操作。
- Laravel 自动触发
二、实现身份验证失败断连
方案 1:使用 Laravel Reverb(官方方案)
Reverb 默认在频道认证失败时拒绝订阅,但不会主动断开连接。需通过以下方式强制断连:
自定义认证中间件:
- 创建中间件
VerifyWebSocketAuth
:php artisan make:middleware VerifyWebSocketAuth
- 编辑中间件逻辑:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Support\Facades\Auth; class VerifyWebSocketAuth { public function handle($request, Closure $next) { if (!Auth::check()) { // 身份验证失败 // 返回 403 并关闭连接 abort(403, 'Unauthorized'); } return $next($request); } }
- 创建中间件
将中间件应用到广播路由:
- 修改
app/Providers/BroadcastServiceProvider.php
:public function boot() { Broadcast::routes(['middleware' => ['web', 'auth:api', VerifyWebSocketAuth::class]]); }
- 修改
前端处理断连:
Echo.private('chat.1') .listen('NewMessage', (data) => { /* ... */ }) .error((error) => { console.error('连接被拒绝:', error); Echo.disconnect(); // 主动断开连接 });
方案 2:使用 Soketi(Pusher 替代)
Soketi 作为 Pusher 协议兼容服务,可通过 自定义授权端点 实现强制断连:
- 配置自定义授权端点:
- 在
.env
中指定授权路由:PUSHER_APP_AUTHORIZER=http://your-app.test/broadcasting/auth
- 在
- 自定义授权响应:
- 在
routes/api.php
中定义严格认证逻辑:use Illuminate\Support\Facades\Broadcast; Broadcast::channel('chat.{userId}', function ($user, $userId) { if ($user->id !== (int) $userId) { // 返回 403 并附带断连指令 return response()->json(['error' => 'Forbidden'], 403) ->header('Connection', 'close'); } return true; });
- 在
方案 3:使用 Workerman/Swoole(底层控制)
若需直接操作 WebSocket 连接,可通过 Workerman/Swoole 的底层 API 强制断连:
- 保存客户端连接标识:
- 在连接建立时记录客户端 ID(如
fd
):$worker->onConnect = function ($connection) { $connection->id = uniqid(); Cache::put('ws_connections.' . $connection->id, $connection); };
- 在连接建立时记录客户端 ID(如
- 认证失败时主动断连:
$worker->onMessage = function ($connection, $data) { $user = Auth::user(); if (!$user) { // 发送错误消息并关闭连接 $connection->send(json_encode(['error' => 'Unauthorized'])); $connection->close(); return; } // 正常处理消息 };
三、生产环境优化
- 日志记录:
// 在中间件或事件中记录断连行为 Log::warning('WebSocket 连接因认证失败被关闭', [ 'ip' => request()->ip(), 'time' => now() ]);
- 速率限制:
- 在
app/Providers/RouteServiceProvider.php
中限制认证请求频率:RateLimiter::for('websocket-auth', function (Request $request) { return Limit::perMinute(30); });
- 在
四、完整示例代码
服务端(Laravel Reverb)
- 频道授权文件 (
routes/channels.php
):Broadcast::channel('chat.{userId}', function ($user, $userId) { if ($user->id !== (int) $userId) { // 抛出异常触发断连 throw new \Exception('身份验证失败'); } return true; });
- 全局异常处理 (
app/Exceptions/Handler.php
):public function register() { $this->renderable(function (\Exception $e, $request) { if ($e->getMessage() === '身份验证失败') { return response()->json(['error' => 'Forbidden'], 403) ->header('Connection', 'close'); } }); }
客户端(Laravel Echo)
// 监听连接错误
Echo.connector.pusher.connection.bind('error', (error) => {
if (error.status === 403) {
Echo.disconnect();
alert('身份验证失败,连接已关闭');
}
});
五、关键注意事项
- 协议兼容性:
- 使用
wss://
协议时确保 SSL 证书有效。
- 使用
- 连接状态同步:
- 使用 Redis 或数据库跟踪在线用户状态。
- 测试工具:
- 使用
WebSocket King
或Postman
手动测试断连逻辑。
- 使用
通过以上方案,可以在 Laravel 12 中实现 WebSocket 通信时的严格身份验证,确保验证失败时主动断开连接,提升系统安全性。