在 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 通信時的嚴格身份驗證,確保驗證失敗時主動斷開連接,提升系統安全性。