avatar

mdo

Hello

  • 首页
  • 知识库
  • 归档
  • 标签
  • 关于
主页 Swoole 的底层特性完成 WebSocket 的握手控制、连接鉴权
文章

Swoole 的底层特性完成 WebSocket 的握手控制、连接鉴权

发表于 2026-06-17 更新于 2026-06- 17
作者 mdo
12~16 分钟 阅读

在 think-swoole 架构中,底层的 HTTP 协议升级(即握手过程)已经被框架封装,开发者通常不需要(也不建议)去重写底层的 onHandshake 回调,而是通过框架提供的**事件系统(Event)**来拦截和处理业务。 [1, 2]


一、 核心:握手阶段的安全鉴权与 UID 绑定 [1]

在标准的实时应用(如聊天室、通知系统)中,我们通常需要限制未登录用户连接,并将用户的 UID 与 Swoole 的客户端连接 ID (fd) 进行映射。 [1, 2, 3]

1. 前端携带 Token 发起握手 [1]

前端在发起 ws:// 握手请求时,无法像标准 HTTP 那样自定义 Headers(浏览器原生 WebSocket API 的限制)。标准的做法是**将 Token 作为 URL 参数或子协议(Subprotocol)**传给后端: [1, 2, 3, 4]

javascript

// 做法:通过 URL 查询参数将 token 发送给 TP6
const token = "your_jwt_token_here";
const ws = new WebSocket(`ws://127.0.0.1:9501?token=${token}`);

请谨慎使用此类代码。

2. 后端在 Connect 事件中做登录拦截 [1]

当 Swoole 收到客户端的握手请求时,框架会解析请求并触发 swoole.websocket.Connect 事件。此时底层连接虽然已经建立,但你可以在这里直接把非法连接直接断开。 [1, 2, 3, 4]

修改或创建你的监听器 app\listener\WsConnect.php: [1]

php

namespace app\listener;

use think\Container;
use think\swoole\Websocket;

class WsConnect
{
    public function handle($event)
    {
        // 1. $event 实际上就是底层的 Swoole\Http\Request 请求对象
        // 我们可以直接获取前端握手时带过来的 GET 参数
        $token = $event->get['token'] ?? '';
        $fd    = $event->fd; // 当前客户端的唯一 fd

        // 2. 执行你的 Token 鉴权逻辑(示例)
        $uid = $this->checkTokenAndGetUid($token);
        
        if (!$uid) {
            // 鉴权失败,直接拒绝!
            echo "客户端 {$fd} 鉴权失败,强制断开。\n";
            
            // 获取 Swoole Server 实例并强制关闭该连接
            $server = app('swoole.server');
            $server->close($fd);
            return false; 
        }

        // 3. 鉴权成功,将 UID 与当前 fd 进行关系绑定
        // 推荐做法:存入 Redis 缓存(哈希表)中
        $redis = app('redis'); 
        $redis->hSet('ws_user_fd', (string)$uid, $fd);
        $redis->hSet('ws_fd_user', (string)$fd, $uid);

        echo "用户 UID:{$uid} (fd:{$fd}) 握手鉴权成功并上线。\n";
    }

    private function checkTokenAndGetUid($token)
    {
        // 模拟解密 Token 并返回用户 id
        if ($token === 'your_jwt_token_here') {
            return 10086; 
        }
        return null;
    }
}

请谨慎使用此类代码。


二、 进阶:如何主动向指定用户发送消息?

由于用户刷新网页时 fd 会发生改变,你在业务层绝不能直接记录固定的 fd。当需要给某个用户发消息时,应当通过 UID 反查 fd。 [1]

示例:在某个普通的 Controller(如订单支付成功后)给用户推送消息: [1]

php

namespace app\controller;

use think\swoole\Websocket;

class Order
{
    public function paySuccess(Websocket $ws)
    {
        $targetUid = 10086; // 目标接收用户
        
        // 从 Redis 中反查该用户的 fd
        $redis = app('redis');
        $fd = $redis->hGet('ws_user_fd', (string)$targetUid);

        if ($fd && app('swoole.server')->exist($fd)) {
            // 使用 think-swoole 提供的 Websocket 实例直接推送到指定 fd
            $ws->to((int)$fd)->emit('notification', [
                'type' => 'order',
                'message' => '您的订单已支付成功!'
            ]);
        }
    }
}

请谨慎使用此类代码。


三、 生产环境避坑指南(Nginx 反向代理配置) [1]

在生产环境中,你的站点通常运行在 80 (HTTP) 或 443 (HTTPS) 端口下。你不可能让前端直接暴露出 Swoole 的内部端口(如 9501)。此时,必须配置 Nginx 协助完成握手代理。 [1, 2]

如果前端连接的是 wss://://yourdomain.com,Nginx 的反向代理配置应如下: [1]

nginx

location /ws {
    proxy_pass http://127.0.0.1:9501; # 指向你 TP6 Swoole 监听的端口
    proxy_http_version 1.1;         # 核心:必须使用 HTTP/1.1 才能升级协议
    
    # 核心:透传 Upgrade 和 Connection 头给 Swoole 完成握手
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    
    # 透传客户端真实 IP
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # 关键防断线配置:避免 Nginx 默认 60 秒没有数据往来就强制关闭连接
    proxy_read_timeout 600s; 
    proxy_send_timeout 600s;
}

请谨慎使用此类代码。


目前你的连接能顺利建立了吗?

技术
许可协议:  CC BY 4.0
分享

相关文章

10月 2, 2026

iOS 侧载(Sideloading)工具

AltStore 和 Sideloadly 都是目前最流行的 iOS 侧载(Sideloading)工具。 简单来说,它们的作用就是让你绕过苹果的官方 App Store,直接把第三方的 IPA 安装包(也就是 iOS 的应用安装文件)安装到你的 iPhone 或 iPad 上。 通过这两个工具,你

9月 28, 2026

注册 Chat Participant

将当前服务接入 VS Code 的 AI 聊天窗口是完全可行的。VS Code 在近期的版本中正式推出了 Chat Extensions API(github.copilot.chat),允许第三方扩展将自定义的 LLM 服务或工具注册到 Copilot Chat 面板中。 结合当前 code_ge

9月 23, 2026

php intelephense 设置 if 空格

在 VS Code 中,如果你使用的是 PHP Intelephense 插件,控制 if 关键字后面是否加空格的设置是由其内置的格式化引擎(基于 PSR-12 标准)决定的。 你可以通过修改 VS Code 的 settings.json 来配置这个行为: 1. 开启 if 后面的空格(默认/推荐

下一篇

websocket 握手

上一篇

ubuntu 自定义指令 执行脚本

最近更新

  • 比尔盖茨的智慧:想多赚钱,就每天循环做这3件事
  • iOS 侧载(Sideloading)工具
  • 程序员越想创业,越不要急着动手
  • 注册 Chat Participant
  • 中秋给在外游子的一封信

热门标签

API CodeGeex Coding Cursor DeepSeek Docker Gitkraken Harness Laravel Management

目录

©2026 mdo. 保留部分权利。