Laravel Echo + Websockets + 私人频道

Dan*_*nte 3 laravel laravel-echo laravel-websockets

我知道我不是第一个为此苦苦挣扎的人。但几天后,经过很多相关的问题,我觉得我的案子值得它自己的问题:)。

我有一个可用的 websocket 解决方案,包括 Laravel Websockets ( https://beyondco.de/docs/laravel-websockets/getting-started/introduction ) 和用于公共频道的 Laravel Echo 。我的客户端应用程序是一个 vue-cli 应用程序,连接到服务器+公共频道上的广播消息效果很好。授权由 Laravel Passport 处理。因此,通过在授权标头中发送承载令牌,后端应用程序可以知道用户是否已通过身份验证。

然而,我正在努力让私人频道正常工作。尝试进行身份验证总是给我这个错误:

Access to XMLHttpRequest at 'https://my-app.test/broadcasting/auth' from origin 'https://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Run Code Online (Sandbox Code Playgroud)

我知道当我遇到服务器问题时会出现此 CORS 错误,因此我尝试在 Insomnia 中调试请求。然而,当模仿 Insomnia 中的请求时,它会给出响应 200 以及预期的结果:

Insomnia 中的模仿身份验证请求

我一直在阅读一些指南和 stackoverflow 问题,但找不到类似的内容。回到它可能是 CORS 问题,但我认为情况并非如此。我的 OPTIONS 请求返回得很好。

为了完整起见,我还添加了一些可能有助于调试的代码。

我的广播服务提供商

public function boot()
{
    Broadcast::routes(['middleware' => ['auth:api']]);

    require base_path('routes/channels.php');
}
Run Code Online (Sandbox Code Playgroud)

我的频道.php

use Illuminate\Support\Facades\Broadcast;

Broadcast::channel('App.User.{id}', function ($user, $id) {
    return (int) $user->id === (int) $id;
});
Run Code Online (Sandbox Code Playgroud)

客户端

this.echoClient = new Echo({
  broadcaster: 'pusher',
  key: process.env.VUE_APP_WEBSOCKETS_APP_ID,
  wsHost: process.env.VUE_APP_WEBSOCKETS_URL,
  wssPort: 6001,
  // forceTLS: true,
  disableStats: true,
  authEndpoint: process.env.VUE_APP_SERVER_URL + '/broadcasting/auth',
  auth: {
    headers: {
      Authorization: "Bearer " + this.$store.state.auth.auth_token
    }
  }
})

// this one works!!
this.echoClient.channel('App.User')
  .listen('UpdatePosts', (e) => {
    console.log('event received')
    console.log(e)
  })

// private channels don't work ... :'(
this.echoClient.private('App.User.' + this.user.id)
  .listen('UpdatePosts', function(e) {
    console.log(e)
  })
Run Code Online (Sandbox Code Playgroud)

Dan*_*nte 8

对于任何在这个问题上苦苦挣扎的人。对我来说,解决方案是将 api 前缀添加到广播::auth 方法中。

public function boot()
{
    Broadcast::routes(['prefix' => 'api', 'middleware' => ['auth:api']]);

    require base_path('routes/channels.php');
}
Run Code Online (Sandbox Code Playgroud)

当然你需要在客户端正确设置 api 前缀:

  authEndpoint: process.env.VUE_APP_SERVER_URL + '/api/broadcasting/auth',
Run Code Online (Sandbox Code Playgroud)

我想区别在于,当你为 Laravel api 添加前缀时,我们会明确告诉服务器放弃 Web 中间件。

我仍然不太明白为什么 Insomnia 的请求会成功,因为没有设置 x-csrf 标头。Insomnia 确实发送了一个 cookie 标头。也许这就是它在那里工作的原因。

编辑

@Tippin 在 laracasts 论坛上提供的解决方案。

补充一下答案,这毕竟是 CORS 问题。

https://github.com/fruitcake/laravel-cors

在广播路由上添加 API 前缀根本不会改变中间件,因此不会将其放在 api 中间件组中。我认为正在发生的事情是您可能安装了 cors 软件包,并且在允许的路径中,您有类似 api/* 的内容,因此只需添加该前缀即可解决您的问题。否则,您可以将默认广播添加到白名单(假设您将该包用于 CORS):

/*
 * You can enable CORS for 1 or multiple paths.
 * Example: ['api/*']
 */
'paths' => ['api/*', 'broadcasting/auth'],
Run Code Online (Sandbox Code Playgroud)

https://github.com/fruitcake/laravel-cors/blob/master/config/cors.php