本文目录导读:

在 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']; // 示例
}
}
最佳实践建议
-
使用 JWT 进行身份验证:
- 简单、安全、无状态
- 可以包含用户信息和过期时间
-
连接超时机制:
// 设置认证超时 $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(), ['*']);
- 日志记录:
// 记录认证信息 error_log("User {$userId} authenticated at " . date('Y-m-d H:i:s'));
这些方案可以根据你的具体需求进行选择和组合,确保 WebSocket 连接的安全性。