You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3 SPA + Laravel Sanctum广播路由授权401问题排查

Vue3 SPA + Laravel Sanctum 私有频道广播401错误

正在尝试为Vue3 SPA搭配Laravel Sanctum后端配置私有频道广播。公开频道(无需授权)可正常运行,但访问私有频道所需的Sanctum保护路由时,客户端收到401错误。使用原生pusher-js库和laravel-echo均出现该问题,已调试多时未解决。

后端配置

/routes/api.php

Route::middleware('auth:sanctum')->group(function () {
    Route::post('/broadcasting/auth', function (Request $request) {
        // 401错误导致此代码无法执行

        \Log::info('hello');

        response()->json('hello');
    });
});

/app/Events/ChatMessageSent.php

<?php

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class ChatMessageSent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $chatSessionId;
    public $chatMessage;

    public function __construct($chatSessionId, $chatMessage)
    {
        $this->chatSessionId = $chatSessionId;
        $this->chatMessage = $chatMessage;
    }

    public function broadcastOn()
    {
        return new PrivateChannel('chat.'.$this->chatSessionId);
    }
}

ChatController.php中的调用方式:

event(new ChatMessageSent($session->id, $message));

BroadcastServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;

class BroadcastServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Broadcast::routes(['middleware' => ['api', 'auth:sanctum']]);
        require base_path('routes/channels.php');
    }
}

/routes/channels.php

<?php

use App\Models\User;
use Illuminate\Support\Facades\Broadcast;
use App\Models\ChatSession;

Broadcast::channel('chat.{chatSessionId}', function ($user, $chatSessionId) {
    $chatSession = ChatSession::find($chatSessionId);
    return $chatSession ? $chatSession->hasParticipant($user) : false;
});

VerifyCsrfToken.php

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    protected $except = [
        '/api/pusher/auth',
        '/api/broadcasting/auth',
    ];
}

已在/config/app.php中启用App\Providers\BroadcastServiceProvider::class。

前端配置

login.vue

const signIn = () => {
  axios.get('/sanctum/csrf-cookie').then((res) => {
    store.authToken = res.config.headers['X-XSRF-TOKEN']
    console.log(store.authToken)
    axios
      .post('/login', form)
      .then(() => {
        store.auth = sessionStorage.auth = 1
        store.signInModal = false
      })
      .catch((er) => {
        state.errors = er.response.data.errors
      })
  })
}

返回的XSRF-Token:

eyJpdiI6Imp0dEc5ck9NU1BZQUlqbUlxYVRoY3c9PSIsInZhbHVlIjoiSUJBNXNJVTg3OTFTN0c2VXluQzJ0Vk5sVGxnenU2aVhNWGE1V2tEMGFBNVBabmZDOFpqRWF6MFN0N0VLUDVRVmtXVFlmOTQ1MlcySFppWUM5eGZRcU1pSDlsN0grVCtRV2ZjYW1ONjFEdDQzT0JOUW4xalkwOGhaWGVXQi8rbHgiLCJtYWMiOiIyZjUyNGEzZTY5MmRiZDgyYzVhZDMyNGJhMjQ3NGFmYmE5ZDZhNDc0NDZiYTY3NTQ4MzFkYWZmMjU1YWNjNTkwIiwidGFnIjoiIn0=

chat.vue(调用广播路由)

import initEcho from '@/lib/laravel-echo.js'

const inquireChatSession = () => {
  axios
    .get(`/api/chat/${props.id}`)
    .then((res) => {
      state.chatSessionId = res.data.id
      state.messages = res.data.messages
      state.loadingSession = false

      const echo = initEcho(store.authToken) // 创建laravel-echo实例并传入authToken,尝试调用authEndpoint(/api/broadcasting/auth)授权时返回401

      echo
        .private(`private-chat.${state.chatSessionId}`)
        .listen('ChatMessageSent', (data) => {
          state.messages.push(data.chatMessage)

          if (state.focusBrowserTab) {
            state.messages.forEach((message) => {
              if (message.sender_id !== store.user.id) {
                message.read_status = 1
              }
            })
          }
        })
    })
    .catch((er) => {
      state.errors = er.response?.data.errors
      state.loadingSession = false
    })
}

laravel-echo.js

import Echo from 'laravel-echo'
import Pusher from 'pusher-js'

window.Pusher = Pusher

export default (token) => {
  return (window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'bf29be46d8eb2ea8ccd4',
    cluster: 'eu',
    forceTLS: true,
    wsPort: 443,
    wssPort: 443,
    enabledTransports: ['ws', 'wss'],
    withCredentials: true,
    authEndpoint: 'http://localhost:8000/api/broadcasting/auth',
    auth: {
      headers: {
        'X-Requested-With': 'XMLHttpRequest',
        Authorization: `Bearer ${token}`,
      },
    },
  }))
}

axios.js

import axios from 'axios'

axios.defaults.withCredentials = true

if (import.meta.env.DEV) {
  axios.defaults.baseURL = 'http://localhost:8000'
}

内容的提问来源于stack exchange,提问作者Artur Müller Romanov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 09:14:50