本文目录导读:

在 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'])
];
});
通过以上实现,你可以确保只有经过授权的用户才能监听到相应的广播频道,保障应用的安全性。