PHP项目Laravel广播频道授权验证

wen PHP项目 3

本文目录导读:

PHP项目Laravel广播频道授权验证

  1. 基础频道授权
  2. 使用 Channel 类(推荐)
  3. 前端授权配置
  4. 自定义授权逻辑
  5. 高级授权场景
  6. 测试授权
  7. 最佳实践
  8. 常见问题解决

在 Laravel 中,广播频道授权验证是通过频道类授权回调来控制的,以下是完整的实现方案:

基础频道授权

routes/channels.php 中定义

<?php
use App\Models\User;
use App\Models\Order;
use Illuminate\Support\Facades\Broadcast;
// 用户私有频道
Broadcast::channel('user.{userId}', function (User $user, $userId) {
    return (int) $user->id === (int) $userId;
});
// 订单频道 - 只有订单相关用户才能监听
Broadcast::channel('order.{orderId}', function (User $user, $orderId) {
    $order = Order::find($orderId);
    if (!$order) {
        return false;
    }
    // 检查用户是否为订单的创建者或管理员
    return $user->id === $order->user_id || $user->isAdmin();
});
// 团队频道
Broadcast::channel('team.{teamId}', function (User $user, $teamId) {
    return $user->teams()->where('teams.id', $teamId)->exists();
});

使用 Channel 类(推荐)

创建频道类

php artisan make:channel OrderChannel

定义频道类 app/Broadcasting/OrderChannel.php

<?php
namespace App\Broadcasting;
use App\Models\Order;
use App\Models\User;
class OrderChannel
{
    /**
     * 创建新的频道实例
     */
    public function __construct()
    {
        //
    }
    /**
     * 验证用户是否可以监听该频道
     */
    public function join(User $user, $orderId)
    {
        $order = Order::findOrFail($orderId);
        // 业务逻辑验证
        return $user->id === $order->user_id 
            || $user->isAdmin() 
            || $order->assigned_to === $user->id;
    }
}

routes/channels.php 中注册

Broadcast::channel('order.{orderId}', \App\Broadcasting\OrderChannel::class);

前端授权配置

Laravel Echo 配置

// resources/js/bootstrap.js
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
    broadcaster: 'pusher',
    key: import.meta.env.VITE_PUSHER_APP_KEY,
    cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
    forceTLS: true,
    // 自定义授权端点
    authEndpoint: '/broadcasting/auth',
    // 自定义授权头(如果需要)
    auth: {
        headers: {
            'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
            'Authorization': `Bearer ${localStorage.getItem('token')}`
        }
    }
});

自定义授权逻辑

自定义授权中间件

// app/Http/Middleware/EnsureTokenIsValid.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class EnsureTokenIsValid
{
    public function handle(Request $request, Closure $next)
    {
        // 验证 token
        if (!$request->hasHeader('Authorization')) {
            return response('Unauthorized', 401);
        }
        $token = str_replace('Bearer ', '', $request->header('Authorization'));
        // 验证 token 逻辑...
        return $next($request);
    }
}

routes/channels.php 中使用中间件

Broadcast::routes(['middleware' => ['auth:api', 'EnsureTokenIsValid']]);

高级授权场景

多条件授权

Broadcast::channel('chat.{roomId}', function (User $user, $roomId) {
    $room = ChatRoom::findOrFail($roomId);
    // 多种授权条件
    $isMember = $room->members()->where('user_id', $user->id)->exists();
    $isPublic = $room->is_public;
    $isModerator = $user->isModeratorOf($room);
    // 返回 true 表示允许访问,也可以返回数组包含用户信息
    if ($isMember || $isPublic || $isModerator) {
        return ['id' => $user->id, 'name' => $user->name];
    }
    return false;
});

动态权限检查

Broadcast::channel('notifications.{userId}', function (User $user, $userId) {
    // 检查用户是否订阅了通知
    $subscription = NotificationSubscription::where([
        'user_id' => $userId,
        'subscriber_id' => $user->id
    ])->first();
    return $subscription !== null;
});

测试授权

编写授权测试

// tests/Feature/BroadcastAuthTest.php
<?php
namespace Tests\Feature;
use App\Models\User;
use App\Models\Order;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class BroadcastAuthTest extends TestCase
{
    use RefreshDatabase;
    /** @test */
    public function user_can_authenticate_to_private_channel()
    {
        $user = User::factory()->create();
        $order = Order::factory()->create(['user_id' => $user->id]);
        $this->actingAs($user)
            ->post('/broadcasting/auth', [
                'channel_name' => 'private-order.' . $order->id,
                'socket_id' => '123456.789012'
            ])
            ->assertSuccessful();
    }
    /** @test */
    public function unauthorized_user_cannot_access_channel()
    {
        $user = User::factory()->create();
        $otherUser = User::factory()->create();
        $order = Order::factory()->create(['user_id' => $otherUser->id]);
        $this->actingAs($user)
            ->post('/broadcasting/auth', [
                'channel_name' => 'private-order.' . $order->id,
                'socket_id' => '123456.789012'
            ])
            ->assertForbidden();
    }
}

最佳实践

安全建议

// 1. 始终验证用户身份
Broadcast::channel('secure.{id}', function (User $user, $id) {
    // 不要信任前端传来的 ID,从认证用户获取
    return $user->id === (int) $id;
});
// 2. 使用资源所有者的 ID
Broadcast::channel('resources.{resourceId}', function (User $user, $resourceId) {
    $resource = Resource::findOrFail($resourceId);
    // 检查用户是否是资源所有者
    return $resource->owner_id === $user->id;
});
// 3. 实现精细的权限控制
Broadcast::channel('projects.{projectId}', function (User $user, $projectId) {
    return $user->hasPermission('view-project', $projectId);
});

性能优化

// 缓存授权结果
Broadcast::channel('data.{tenantId}', function (User $user, $tenantId) {
    return Cache::remember("channel-auth-{$user->id}-{$tenantId}", 60, function () use ($user, $tenantId) {
        return $user->tenants()->where('id', $tenantId)->exists();
    });
});

常见问题解决

跨域问题

// config/broadcasting.php
'connections' => [
    'pusher' => [
        // ...
        'options' => [
            'cluster' => 'mt1',
            'useTLS' => true,
            'curl_options' => [
                CURLOPT_SSL_VERIFYPEER => false,
            ],
        ],
    ],
],

自定义认证响应

// 在 AppServiceProvider 中
use Illuminate\Support\Facades\Broadcast;
Broadcast::auth(function ($request) {
    // 自定义认证逻辑
    return [
        'user' => $request->user()->only(['id', 'name', 'email'])
    ];
});

通过以上实现,你可以确保只有经过授权的用户才能监听到相应的广播频道,保障应用的安全性。

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