1

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

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

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

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.

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

在 Insomnia 中模拟身份验证请求

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

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

我的广播服务提供者

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

    require base_path('routes/channels.php');
}

我的频道.php

use Illuminate\Support\Facades\Broadcast;

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

客户端

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)
  })
4

2 回答 2

3

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

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

    require base_path('routes/channels.php');
}

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

  authEndpoint: process.env.VUE_APP_SERVER_URL + '/api/broadcasting/auth',

我想不同之处在于,当您为 api Laravel 加上前缀时,我们会明确告诉服务器放弃 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'],

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

于 2020-12-24T23:29:28.300 回答
2

我有同样的问题通过使用fruitcake/laravel-cors,它已经解决了。

这是我的身份验证选项:

    auth : {
        headers : {
             Authorization: "Bearer " + token,
             Accept: "application/json",

        }
    },
于 2020-12-24T11:48:22.947 回答