PHP WebSocket 权限验证

wen PHP项目 1

本文目录导读:

PHP WebSocket 权限验证

  1. Token 验证方案(推荐)
  2. 基于 Redis 的 Session 验证
  3. 双向认证方案
  4. 客户端 JavaScript 实现
  5. 权限控制完整示例
  6. 最佳实践建议

在 PHP WebSocket 中进行权限验证,主要有以下几种常见方案:

Token 验证方案(推荐)

客户端连接时携带 Token

// 客户端连接 WebSocket 时
const ws = new WebSocket('ws://example.com:8080?token=YOUR_AUTH_TOKEN');

服务端验证

<?php
// WebSocket 服务端代码(使用 Workerman 或 Ratchet)
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class ChatServer implements MessageComponentInterface {
    protected $clients;
    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }
    public function onOpen(ConnectionInterface $conn) {
        // 获取 Token
        $queryParams = [];
        parse_str(parse_url($conn->httpRequest->getUri(), PHP_URL_QUERY), $queryParams);
        $token = $queryParams['token'] ?? '';
        // 验证 Token
        if (!$this->validateToken($token)) {
            $conn->close();
            return;
        }
        // 将用户信息绑定到连接
        $conn->user_id = $this->getUserIdFromToken($token);
        $conn->token = $token;
        $this->clients->attach($conn);
        echo "新连接: {$conn->resourceId}\n";
    }
    private function validateToken($token) {
        // 从数据库或缓存中验证 Token
        // 示例:Redis 或 JWT 验证
        if (empty($token)) {
            return false;
        }
        // JWT 验证示例
        try {
            $decoded = JWT::decode($token, JWT_SECRET, ['HS256']);
            return true;
        } catch (\Exception $e) {
            return false;
        }
    }
    private function getUserFromToken($token) {
        // 通过 Token 获取用户信息
        return JWT::decode($token, JWT_SECRET, ['HS256']);
    }
}

基于 Redis 的 Session 验证

<?php
class WebSocketAuth {
    public function authenticate(ConnectionInterface $conn) {
        $sessionId = $this->getSessionId($conn);
        // 从 Redis 获取 Session 数据
        $redis = new Redis();
        $redis->connect('127.0.0.1', 6379);
        $sessionData = $redis->get("PHPREDIS_SESSION:$sessionId");
        if (!$sessionData) {
            return false;
        }
        // 解析 Session 数据
        $userData = session_decode($sessionData);
        if (!isset($userData['user_id'])) {
            return false;
        }
        $conn->user_id = $userData['user_id'];
        return true;
    }
    private function getSessionId($conn) {
        // 从请求中获取 Session ID
        $headers = $conn->httpRequest->getHeaders();
        $cookieHeader = $headers['Cookie'][0] ?? '';
        preg_match('/PHPSESSID=([^;]+)/', $cookieHeader, $matches);
        return $matches[1] ?? '';
    }
}

双向认证方案

<?php
class AuthWebSocketServer implements MessageComponentInterface {
    public function onOpen(ConnectionInterface $conn) {
        // 初始状态:未认证
        $conn->authenticated = false;
        // 收到认证请求后
        $conn->send(json_encode([
            'type' => 'auth_required'
        ]));
    }
    public function onMessage(ConnectionInterface $from, $msg) {
        $data = json_decode($msg, true);
        // 检查是否是认证请求
        if (!$from->authenticated) {
            $this->handleAuth($from, $data);
            return;
        }
        // 已认证的连接处理正常消息
        $this->handleMessage($from, $data);
    }
    private function handleAuth(ConnectionInterface $conn, $data) {
        if ($data['type'] === 'authenticate') {
            $token = $data['token'] ?? '';
            // 验证 Token
            if ($this->validateToken($token)) {
                $conn->authenticated = true;
                $conn->user = $this->getUser($token);
                $conn->send(json_encode([
                    'type' => 'authenticated',
                    'data' => ['user' => $conn->user]
                ]));
            } else {
                $conn->send(json_encode([
                    'type' => 'auth_failed',
                    'message' => 'Invalid token'
                ]));
                $conn->close();
            }
        }
    }
}

客户端 JavaScript 实现

// 客户端认证
class WebSocketClient {
    constructor(token) {
        this.token = token;
        this.connected = false;
        this.initWebSocket();
    }
    initWebSocket() {
        this.ws = new WebSocket('ws://localhost:8080');
        this.ws.onopen = () => {
            console.log('正在建立 WebSocket 连接...');
        };
        this.ws.onmessage = (event) => {
            const data = JSON.parse(event.data);
            if (data.type === 'auth_required') {
                // 发送认证请求
                this.authenticate();
            } else if (data.type === 'authenticated') {
                this.connected = true;
                console.log('WebSocket 认证成功');
                this.onAuthenticated(data.data);
            } else if (data.type === 'auth_failed') {
                console.error('WebSocket 认证失败');
                this.onAuthFailed(data.message);
            } else {
                // 处理正常消息
                this.handleMessage(data);
            }
        };
        this.ws.onerror = (error) => {
            console.error('WebSocket 错误:', error);
        };
        this.ws.onclose = () => {
            console.log('WebSocket 连接关闭');
            this.connected = false;
            this.reconnect();
        };
    }
    authenticate() {
        this.ws.send(JSON.stringify({
            type: 'authenticate',
            token: this.token
        }));
    }
    send(data) {
        if (this.connected) {
            this.ws.send(JSON.stringify(data));
        } else {
            console.error('WebSocket 未认证');
        }
    }
    reconnect() {
        // 重连逻辑
        setTimeout(() => {
            this.initWebSocket();
        }, 3000);
    }
}
// 使用
const client = new WebSocketClient('your_jwt_token');

权限控制完整示例

<?php
class SecureWebSocketServer implements MessageComponentInterface {
    private $clients;
    public function onOpen(ConnectionInterface $conn) {
        $this->clients[$conn->resourceId] = $conn;
        $conn->permissions = [];
    }
    public function onMessage(ConnectionInterface $from, $msg) {
        $data = json_decode($msg, true);
        // 权限检查
        if (!$this->checkPermission($from, $data['action'] ?? '')) {
            $from->send(json_encode([
                'type' => 'error',
                'message' => '权限不足'
            ]));
            return;
        }
        // 处理消息
        $this->processMessage($from, $data);
    }
    private function checkPermission($conn, $action) {
        // 获取用户权限列表
        $permissions = $this->getUserPermissions($conn->user_id);
        // 检查动作权限
        switch ($action) {
            case 'send_message':
                return in_array('send_message', $permissions);
            case 'delete_message':
                return in_array('admin', $permissions);
            default:
                return false;
        }
    }
    private function getUserPermissions($userId) {
        // 从数据库或缓存获取权限
        return ['send_message', 'read_messages']; // 示例
    }
}

最佳实践建议

  1. 使用 JWT 进行身份验证

    • 简单、安全、无状态
    • 可以包含用户信息和过期时间
  2. 连接超时机制

    // 设置认证超时
    $conn->authTimeout = time() + 10; // 10秒内必须完成认证

// 定期检查 if (time() > $conn->authTimeout && !$conn->authenticated) { $conn->close(); }


3. **使用 HTTPS/WSS**:
```php
// SSL 配置
$server = new Ratchet\App('localhost', 8080, '0.0.0.0');
$server->route('/chat', new ChatServer(), ['*']);
  1. 日志记录
    // 记录认证信息
    error_log("User {$userId} authenticated at " . date('Y-m-d H:i:s'));

这些方案可以根据你的具体需求进行选择和组合,确保 WebSocket 连接的安全性。

抱歉,评论功能暂时关闭!