×
贴子
课程
动态
签到
更新
直播间
系统
碎片
订阅
话题
文档
序言
发布说明
升级指南
贡献指南
开始使用
安装
配置
目录结构
Homestead
Valet
部署
核心架构
请求生命周期
服务容器
服务提供者
Facades
Contracts
基础功能
路由
中间件
CSRF 保护
控制器
请求
响应
视图
URL
表单验证
Session
错误处理
日志
前端开发
Blade 模板
本地化
前端指南
编辑资源 Mix
安全相关
用户认证
用户授权
Email 授权
加密解密
哈希
重置密码
深层发掘
Artisan 命令行
广播系统
缓存系统
集合
事件系统
文件存储
辅助函数
邮件发送
消息通知
扩展包开发
队列
任务调度
数据库
入门
查询构造器
分页
数据库迁移
数据填充
Redis
Eloquent ORM
入门
模型关联
Eloquent 集合
修改器
API 资源
序列化
测试相关
入门指南
HTTP测试
控制台测试
浏览器测试(Laravel Dusk)
数据库测试
测试模拟器
官方扩展包
Cashier
Envoy 部署工具
Horizon
Scout
Laravel 社会化登录
深层发掘
Laravel 5.7 中文文档
/
广播系统
# 广播系统 ## 简介 在许多现代Web应用程序中,WebSockets用于实现实时,实时更新的用户界面。当在服务器上更新某些数据时,通常通过WebSocket连接发送消息以由客户端处理。这为持续轮询应用程序的更改提供了更强大,更有效的替代方案。 为了帮助您构建这些类型的应用程序,Laravel可以轻松地通过WebSocket连接“广播”您的[事件](https://laravel.com/docs/5.7/events)。通过广播Laravel事件,您可以在服务器端代码和客户端JavaScript应用程序之间共享相同的事件名称。 > 在深入了解事件广播之前,请确保您已阅读有关Laravel [事件和监听](https://laravel.com/docs/5.7/events)所有文档。 ### 配置 您的所有应用程序的事件广播配置都存储在`config/broadcasting.php`配置文件中。Laravel支持多种开箱即用的广播驱动程序:[Pusher](https://pusher.com/),[Redis](https://laravel.com/docs/5.7/redis)以及用于本地开发和调试的`log`驱动程序。此外,还包括一个`null`驱动程序,允许您完全禁用广播。`config/broadcasting.php`配置文件中包含每个驱动程序的配置示例。 #### 广播服务提供者 在播放任何活动之前,您首先需要注册`App\Providers\BroadcastServiceProvider`。在新的Laravel应用程序中,您只需要在`config/app.php`配置文件的`providers`数组中取消注释此提供程序。此提供程序将允许您注册广播授权路由和回调。 #### CSRF令牌 [Laravel Echo](https://laravel.com/docs/5.7/broadcasting#installing-laravel-echo)将需要访问当前会话的CSRF令牌。您应该验证应用程序的`head`HTML元素是否定义了`meta`包含CSRF令牌的标记: ```php <meta name="csrf-token" content="{{ csrf_token() }}"> ``` ### 对驱动的要求 #### Pusher 如果您通过[Pusher](https://pusher.com/)广播事件,则应使用Composer包管理器安装Pusher PHP SDK: ```php composer require pusher/pusher-php-server "~3.0" ``` 接下来,您应该在`config/broadcasting.php`配置文件中配置Pusher凭据。此文件中已包含示例Pusher配置,允许您快速指定Pusher key 、secret 和 application ID。 。`config/broadcasting.php`文件的`pusher`配置还允许您指定Pusher支持的其他`options`,例如群集 ```php 'options' => [ 'cluster' => 'eu', 'encrypted' => true ], ``` 使用Pusher和[Laravel Echo时](https://laravel.com/docs/5.7/broadcasting#installing-laravel-echo),`pusher`在`resources/js/bootstrap.js`文件中实例化Echo实例时,应指定为所需的广播器: ```php import Echo from "laravel-echo" window.Pusher = require('pusher-js'); window.Echo = new Echo({ broadcaster: 'pusher', key: 'your-pusher-key' }); ``` #### Redis 如果您使用的是Redis广播器,则应安装Predis库: ```php composer require predis/predis ``` Redis广播器将使用Redis的pub / sub功能广播消息; 但是,您需要将其与WebSocket服务器配对,该服务器可以从Redis接收消息并将它们广播到您的WebSocket频道。 当Redis广播器发布一个事件时,它将在事件的指定通道名称上发布,并且有效负载将是一个JSON编码的字符串,其中包含事件名称,`data`有效负载和生成事件套接字ID的用户(如果适用)。 #### Socket.IO 如果要将Redis广播器与Socket.IO服务器配对,则需要在应用程序中包含Socket.IO JavaScript客户端库。您可以通过NPM包管理器安装它: ```php npm install --save socket.io-client ``` 接下来,你需要在实例化 Echo 时指定 `socket.io` 连接器和 `host` 。 ```php import Echo from "laravel-echo" window.io = require('socket.io-client'); window.Echo = new Echo({ broadcaster: 'socket.io', host: window.location.hostname + ':6001' }); ``` 最后,您需要运行兼容的Socket.IO服务器。Laravel不包含Socket.IO服务器实现; 但是,社区驱动的Socket.IO服务器目前在[tlaverdure / laravel-echo-server](https://github.com/tlaverdure/laravel-echo-server) GitHub存储库中维护。 #### 队列先决条件 在广播事件之前,您还需要配置并运行[队列监听器](https://laravel.com/docs/5.7/queues)。所有事件广播都是通过排队的作业完成的,这样您的应用程序的响应时间就不会受到严重影响。 ## 概念概述 Laravel的事件广播允许您使用基于驱动程序的WebSockets方法将服务器端Laravel事件广播到客户端JavaScript应用程序。目前,Laravel与[Pusher](https://pusher.com/)和Redis司机一起发货。使用[Laravel Echo](https://laravel.com/docs/5.7/broadcasting#installing-laravel-echo) Javascript包可以在客户端轻松使用这些事件。 事件通过“频道”广播,可以指定为公共或私人。您的申请的任何访问者都可以在没有任何认证或授权的情况下订阅公共频道; 但是,为了订阅私人频道,必须对用户进行身份验证并授权其在该频道上进行收听。 ### 使用示例应用程序 在深入了解事件广播的每个组成部分之前,让我们以电子商务商店为例进行高级概述。我们不会讨论配置[Pusher](https://pusher.com/)或[Laravel Echo](https://laravel.com/docs/5.7/broadcasting#installing-laravel-echo)的细节,因为这将在本文档的其他部分中详细讨论。 在我们的应用程序中,假设我们有一个页面,允许用户查看其订单的运送状态。我们还假设`ShippingStatusUpdated`在应用程序处理发货状态更新时触发事件: ```php event(new ShippingStatusUpdated($update)); ``` #### `ShouldBroadcast`接口 当用户查看其中一个订单时,我们不希望他们必须刷新页面以查看状态更新。相反,我们希望在创建应用程序时广播更新。因此,我们需要`ShippingStatusUpdated`使用`ShouldBroadcast`界面标记事件。这会让 Laravel 在事件被触发时广播该事件: ```php <?php namespace App\Events; use Illuminate\Broadcasting\Channel; use Illuminate\Queue\SerializesModels; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; class ShippingStatusUpdated implements ShouldBroadcast { /** * Information about the shipping status update. * * @var string */ public $update; } ``` `ShouldBroadcast`接口需要我们的事件定义`broadcastOn`方法。此方法负责返回事件应广播的频道。已经在生成的事件类上定义了此方法的空存根,因此我们只需要填写其详细信息。我们只希望订单的创建者能够查看状态更新,所以我们要把该事件广播到与这个订单绑定的私有频道上去: ```php /** * Get the channels the event should broadcast on. * * @return array */ public function broadcastOn() { return new PrivateChannel('order.'.$this->update->order_id); } ``` #### 授权频道 记住,用户只有在被授权之后才能监听私有频道。我们可以在 `routes/channels.php` 文件中定义频道的授权规则。在本例中,我们需要对视图监听私有 `order.1` 频道的所有用户进行验证,确保只有订单真正的创建者才能监听: ```php Broadcast::channel('order.{orderId}', function ($user, $orderId) { return $user->id === Order::findOrNew($orderId)->user_id; }); ``` `channel` 方法接收两个参数:频道名称和一个回调函数,该回调通过返回 `true` 或者 `false` 来表示用户是否被授权监听该频道。 所有的授权回调接收当前被认证的用户作为第一个参数,任何额外的通配符参数作为后续参数。在本例中,我们使用 `{orderId}` 占位符来表示频道名称的 「ID」 部分是通配符。 #### 监听事件广播 接下来,剩下的就是在我们的JavaScript应用程序中监听事件。我们可以使用Laravel Echo来做到这一点。首先,我们将使用该`private`方法订阅私人频道。然后,我们可以使用该`listen`方法来监听`ShippingStatusUpdated`事件。默认情况下,所有事件的公共属性都将包含在广播事件中: ```php Echo.private(`order.${orderId}`) .listen('ShippingStatusUpdated', (e) => { console.log(e.update); }); ``` ## 定义广播事件 要通知Laravel应该广播给定事件,请在事件类上实现`Illuminate\Contracts\Broadcasting\ShouldBroadcast`接口。此接口已导入到框架生成的所有事件类中,因此您可以轻松地将其添加到任何事件中。 `ShouldBroadcast` 接口要求你实现一个方法: `broadcastOn`。 该方法返回一个频道或者一个频道数组,事件会被广播到这些频道。这些频道必须是 `Channel` 、`PrivateChannel` 或者 `PresenceChannel` 的实例。 `Channel` 代表任何用户都可以订阅的公开频道, 而 `PrivateChannels` 和 `PresenceChannels`则代表需要[频道授权](https://laravel.com/docs/5.7/broadcasting#authorizing-channels):的私有频道: ```php <?php namespace App\Events; use Illuminate\Broadcasting\Channel; use Illuminate\Queue\SerializesModels; use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; class ServerCreated implements ShouldBroadcast { use SerializesModels; public $user; /** * Create a new event instance. * * @return void */ public function __construct(User $user) { $this->user = $user; } /** * Get the channels the event should broadcast on. * * @return Channel|array */ public function broadcastOn() { return new PrivateChannel('user.'.$this->user->id); } } ``` 然后,您只需像往常一样[触发事件](https://laravel.com/docs/5.7/events)。事件触发后,[队列任务](https://laravel.com/docs/5.7/queues)将自动通过指定的广播驱动程序广播事件。 ### 广播名称 默认情况下,Laravel将使用事件的类名来广播事件。但是,您可以通过`broadcastAs`在事件上定义方法来自定义广播名称: ```php /** * The event's broadcast name. * * @return string */ public function broadcastAs() { return 'server.created'; } ``` 如果使用该`broadcastAs`方法自定义广播名称,则应确保使用前导`.`字符注册侦听器。这将指示Echo不要将应用程序的命名空间添加到事件中: ```php .listen('.server.created', function (e) { .... }); ``` ### 广播数据 广播事件时,其所有`public`属性都会自动序列化并作为事件的有效负载进行广播,允许您从JavaScript应用程序访问其任何公共数据。因此,例如,如果您的事件具有`$user`包含Eloquent模型的单个公共属性,则事件的广播有效负载将为: ```php { "user": { "id": 1, "name": "Patrick Stewart" ... } } ``` 但是,如果您希望对广播有效负载进行更细粒度的控制,则可以为`broadcastWith`事件添加方法。此方法应返回您希望作为事件有效内容广播的数据数组: ```php /** * Get the data to broadcast. * * @return array */ public function broadcastWith() { return ['id' => $this->user->id]; } ``` ### 广播队列 默认情况下,每个广播事件都放在`queue.php`配置文件中指定的默认队列连接的缺省队列中。您可以通过在事件类上定义`broadcastQueue`属性来自定义广播公司使用的队列。此属性应指定广播时要使用的队列的名称: ```php /** * The name of the queue on which to place the event. * * @var string */ public $broadcastQueue = 'your-queue-name'; ``` 如果要使用`sync`队列而不是默认队列驱动程序广播事件,则可以实现`ShouldBroadcastNow`接口而不是`ShouldBroadcast`: ```php <?php use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; class ShippingStatusUpdated implements ShouldBroadcastNow { // } ``` ### 广播条件 有时您只想在给定条件为真时广播您的事件。您可以通过向`broadcastWhen`事件类添加方法来定义这些条件: ```php /** * Determine if this event should broadcast. * * @return bool */ public function broadcastWhen() { return $this->value > 100; } ``` ### 授权频道 私有频道要求您授权当前经过身份验证的用户可以实际收听频道。实现过程是用户向你的 Laravel 应用程序发起一个携带频道名称的 HTTP 请求,由你的应用程序判断该用户是否能够监听该频道。在使用[Laravel Echo时](https://laravel.com/docs/5.7/broadcasting#installing-laravel-echo)时,授权订阅私有频道的 HTTP 请求会自动发送;尽管如此,你仍需定义相应的路由来响应这些请求。 ### 定义授权路由 值得庆幸的是,Laravel可以轻松定义响应频道授权请求的路由。在`BroadcastServiceProvider`Laravel应用程序附带的内容中,您将看到对`Broadcast::routes`方法的调用。该方法会注册 `/broadcasting/auth` 路由来处理授权请求: ```php Broadcast::routes(); ``` `Broadcast::routes`方法将自动将其路由放在`web`中间件组中; 但是,如果要自定义指定的属性,可以将路径属性数组传递给方法: ```php Broadcast::routes($attributes); ``` ### 定义授权回调 接下来,我们需要定义实际执行通道授权的逻辑。这是在您的应用程序附带的`routes/channels.php`文件中完成的。在此文件中,您可以使用`Broadcast::channel`方法注册通道授权回调: ```php Broadcast::channel('order.{orderId}', function ($user, $orderId) { return $user->id === Order::findOrNew($orderId)->user_id; }); ``` `channel` 方法接收两个参数:频道名称和一个回调函数,该回调通过返回 `true` 或者 `false` 来表示用户是否被授权监听该频道。 所有的授权回调接收当前认证用户作为第一个参数,任何额外的通配符参数作为后续参数。在本例中,我们使用 `{orderId}` 占位符来表示频道名称的 「ID」 部分是通配符。 #### 授权回调模型绑定 就像HTTP路由一样,通道路由也可以利用隐式和显式[路由模型绑定](https://laravel.com/docs/5.7/routing#route-model-binding)。例如,您可以请求实际的`Order`模型实例,而不是接收字符串或数字订单ID : ```php use App\Order; Broadcast::channel('order.{order}', function ($user, Order $order) { return $user->id === $order->user_id; }); ``` ### 定义频道类 如果您的应用程序消耗了许多不同的频道,您的`routes/channels.php`文件可能会变得笨重。因此,您可以使用通道类,而不是使用Closures来授权通道。要生成通道类,请使用`make:channel`Artisan命令。此命令将在`App/Broadcasting`目录中放置一个新的通道类。 ```php php artisan make:channel OrderChannel ``` 接下来,在您的`routes/channels.php`文件中注册您的频道: ```php use App\Broadcasting\OrderChannel; Broadcast::channel('order.{order}', OrderChannel::class); ``` 最后,您可以将通道的授权逻辑放在通道类的`join`方法中。此`join`方法将保留您通常放置在通道授权关闭中的逻辑。当然,您也可以利用通道模型绑定: ```php <?php namespace App\Broadcasting; use App\User; use App\Order; class OrderChannel { /** * Create a new channel instance. * * @return void */ public function __construct() { // } /** * Authenticate the user's access to the channel. * * @param \App\User $user * @param \App\Order $order * @return array|bool */ public function join(User $user, Order $order) { return $user->id === $order->user_id; } } ``` > 与Laravel中的许多其他类一样,通道类将由[服务容器](https://laravel.com/docs/5.7/container)自动解析。因此,您可以在其构造函数中键入提示您的通道所需的任何依赖项。 ## 广播事件 定义事件并使用`ShouldBroadcast`界面标记事件后,您只需使用该`event`函数触发事件。事件调度程序将注意到事件已标记为`ShouldBroadcast`接口,并将事件排队以进行广播: ```php event(new ShippingStatusUpdated($update)); ``` ### 只广播给他人 在构建利用事件广播的应用程序时,您可以`event`使用该`broadcast`功能替换该功能。与`event`函数一样,该`broadcast`函数将事件调度到服务器端侦听器: ```php broadcast(new ShippingStatusUpdated($update)); ``` 但是,该`broadcast`功能还公开了`toOthers`允许您从广播的收件人中排除当前用户的方法: ```php broadcast(new ShippingStatusUpdated($update))->toOthers(); ``` 为了更好地理解什么时候使用 `toOthers` 方法,让我们假设有一个任务列表的应用程序,用户可以通过输入任务名来新建任务。要新建任务,你的应用程序需要发起一个请求到一个 `/task` 路由,该路由会广播任务的创建,并返回新任务的 JSON 响应。当你的 JavaScript 应用程序从路由收到响应后,它会直接将新任务插入到任务列表中,就像这样: ```php axios.post('/task', task) .then((response) => { this.tasks.push(response.data); }); ``` 但是,请记住,我们还广播任务的创建。如果您的JavaScript应用程序正在侦听此事件以便将任务添加到任务列表,则列表中将出现重复的任务:一个来自路由响应,另一个来自广播。你可以通过使用 `toOthers` 方法告知广播器不要将事件广播到当前用户来解决这个问题。 > 您的事件必须使用`Illuminate\Broadcasting\InteractsWithSockets` trait才能调用`toOthers`方法。 ### 配置 初始化Laravel Echo实例时,会为连接分配套接字ID。如果您使用的是[Vue](https://vuejs.org/)和[Axios](https://github.com/mzabriskie/axios),则套接字ID将自动作为`X-Socket-ID`标头附加到每个传出请求。然后,当您调用`toOthers`方法时,Laravel将从标头中提取套接字ID,并告知广播器不要广播任何消息到带有这个套接字 ID 的连接上。 如果您不使用Vue和Axios,则需要手动配置JavaScript应用程序以发送`X-Socket-ID`头。您可以使用`Echo.socketId`方式检索套接字ID : ```php var socketId = Echo.socketId(); ``` ## 接受广播 ### 安装Laravel echo Laravel Echo是一个JavaScript库,可以轻松订阅频道并收听Laravel播放的事件。您可以通过NPM包管理器安装Echo。在本例中,因为我们会使用 Pusher 广播器,所以我们也会安装 `pusher-js` 包: ```php npm install --save laravel-echo pusher-js ``` 安装Echo后,您就可以在应用程序的JavaScript中创建一个全新的Echo实例。一个很好的地方是在Laravel框架附带的`resources/js/bootstrap.js`文件的底部: ```php import Echo from "laravel-echo" window.Echo = new Echo({ broadcaster: 'pusher', key: 'your-pusher-key' }); ``` 创建使用`pusher`连接器的Echo实例时,您还可以指定`cluster`连接是否应加密: ```php window.Echo = new Echo({ broadcaster: 'pusher', key: 'your-pusher-key', cluster: 'eu', encrypted: true }); ``` ### 监听事件 一旦安装并实例化了Echo,就可以开始监听事件广播了。首先,使用该`channel`方法检索通道的实例,然后调用该`listen`方法以侦听指定的事件: ```php Echo.channel('orders') .listen('OrderShipped', (e) => { console.log(e.order.name); }); ``` 如果您想在私人频道上收听活动,请改用该`private`方法。您可以继续将调用链接到该`listen`方法以在单个通道上侦听多个事件: ```php Echo.private('orders') .listen(...) .listen(...) .listen(...); ``` ### 退出频道 要退出频道,可以在你的 Echo 实例上调用 `leave` 方法: ```php Echo.leave('orders'); ``` ### 命名空间 您可能已经在上面的示例中注意到我们没有为事件类指定完整的命名空间。这是因为 Echo 会默认事件都在 `App\Events` 命名空间下。但是,你可以在实例化 Echo 时传递一个 `namespace` 配置项来指定根命名空间: ```php window.Echo = new Echo({ broadcaster: 'pusher', key: 'your-pusher-key', namespace: 'App.Other.Namespace' }); ``` 另外,你可以在使用 Echo 订阅事件的时候为事件类加上 `.` 前缀。这样就可以指定完全限定名称的类名了: ```php Echo.channel('orders') .listen('.Namespace.Event.Class', (e) => { // }); ``` ## Presence频道 在线通道建立在私人频道的安全性的基础上,同时暴露了谁订阅频道的附加功能。这样可以轻松构建功能强大的协作应用程序功能,例如在其他用户查看同一页面时通知用户。 ### 授权Presence频道 所有Presence频道也是私有频道; 因此,必须[授权](https://laravel.com/docs/5.7/broadcasting#authorizing-channels)用户[访问它们](https://laravel.com/docs/5.7/broadcasting#authorizing-channels)。但是,在为状态通道定义授权回调时,如果一个用户已经加入了该频道,那么不应该返回 `true` ,而应该返回一个关于该用户信息的数组。 授权回调返回的数据将可供JavaScript应用程序中的状态通道事件侦听器使用。如果用户未被授权加入在线频道,您应该返回`false`或`null`: ```php Broadcast::channel('chat.{roomId}', function ($user, $roomId) { if ($user->canJoinRoom($roomId)) { return ['id' => $user->id, 'name' => $user->name]; } }); ``` ### 加入Presence频道 要加入状态通道,您可以使用Echo的`join`方法。该`join`方法将返回一个`PresenceChannel`实现,通过暴露 `listen` 方法,可以让你订阅`here`,`joining`和`leaving`事件。 ```php Echo.join(`chat.${roomId}`) .here((users) => { // }) .joining((user) => { console.log(user.name); }) .leaving((user) => { console.log(user.name); }); ``` 该`here`回调将被立即执行,一旦信道被成功地加入,并且将接收包含所有当前订阅了该信道的其它用户的用户信息的数组。该`joining`方法将在新用户加入频道时`leaving`执行,而该方法将在用户离开频道时执行。 ### 广播到Presence频道 Presence频道可以像公共或私人频道一样接收事件。使用聊天室的示例,我们可能希望将`NewMessage`事件广播到房间的在线频道。为此,我们将从`PresenceChannel`事件的`broadcastOn`方法返回一个实例: ```php /** * Get the channels the event should broadcast on. * * @return Channel|array */ public function broadcastOn() { return new PresenceChannel('room.'.$this->message->room_id); } ``` 与公共或私人事件一样,可以使用该`broadcast`功能广播存在信道事件。与其他事件一样,您可以使用该`toOthers`方法排除当前用户接收广播: ```php broadcast(new NewMessage($message)); broadcast(new NewMessage($message))->toOthers(); ``` 您可以通过Echo的`listen`方法监听加入事件: ```php Echo.join(`chat.${roomId}`) .here(...) .joining(...) .leaving(...) .listen('NewMessage', (e) => { // }); ``` ## 客户端事件 > 使用[Pusher时](https://pusher.com/),必须在[应用程序仪表板](https://dashboard.pusher.com/)的“应用程序设置”部分中启用“客户端事件”选项才能发送客户端事件。 有时您可能希望将事件广播到其他连接的客户端,而根本不需要点击您的Laravel应用程序。这对于诸如“键入”通知之类的内容特别有用,在这种情况下,您希望提醒应用程序的用户另一个用户在给定屏幕上键入消息。 要广播客户端事件,您可以使用Echo的`whisper`方法: ```php Echo.private('chat') .whisper('typing', { name: this.user.name }); ``` 要侦听客户端事件,您可以使用以下`listenForWhisper`方法: ```php Echo.private('chat') .listenForWhisper('typing', (e) => { console.log(e.name); }); ``` ## 通知 通过将事件广播与[通知](https://laravel.com/docs/5.7/notifications)配对,您的JavaScript应用程序可以在发生时收到新通知,而无需刷新页面。首先,请务必阅读有关使用[广播通知频道](https://laravel.com/docs/5.7/notifications#broadcast-notifications)的文档。 配置通知以使用广播频道后,您可以使用Echo的`notification`方法收听广播事件。请记住,频道名称应与接收通知的实体的类名相匹配: ```php Echo.private(`App.User.${userId}`) .notification((notification) => { console.log(notification.type); }); ``` 在本例中,所有通过 `broadcast` 频道发送到 `App\User` 实例的消息通知都会被回调接收。一个针对 `App.User.{id}` 频道的授权回调函数已经包含在 Laravel 框架内置的 `BroadcastServiceProvider` 中了。