Laravel Broadcasting - частные каналы, доступные без токена на предъявителя

У меня есть настройка socket.io с laravel-echo, чтобы присоединяться и слушать каналы вещания laravel. Публичные каналы работают нормально, так как не требуют авторизации. Частные каналы не работают должным образом, я могу подключиться к любому частному каналу с клиентом socket.io без передачи токена авторизации.

Клиент Socket.io

window.Echo = new Echo({
    host: "http://127.0.0.1:6001",
    auth:{
        headers: {
            Accept: 'application/json',
            Authorization: 'Bearer ',
        },
    },
    broadcaster: 'socket.io',
});

window.Echo.private('user'+"."+userid)
.listen('Notification', (e) => {
    console.log(e);
})

Конфигурация Laravel-Echo-Server

{
"authHost": "http://127.0.0.1:8000",
"authEndpoint": "/broadcasting/auth",
"clients": [],
"database": "redis",
"databaseConfig": {
    "redis": {
        "port": "6379",
        "host": "localhost"
    },
    "sqlite": {}
},
"devMode": true,
"host": null,
"port": "6001",
"protocol": "http",
"socketio": {},
"secureOptions": 67108864,
"sslCertPath": "",
"sslKeyPath": "",
"sslCertChainPath": "",
"sslPassphrase": "",
"subscribers": {
    "http": true,
    "redis": true
},
"apiOriginAllow": {
    "allowCors": true,
    "allowOrigin": "localhost",
    "allowMethods": "GET, POST",
    "allowHeaders": "Origin, Content-Type, X-Auth-Token, X-Requested-With, Accept, Authorization, X-CSRF-TOKEN, X-Socket-Id"
}
}

Маршруты каналов

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

BroadcastServiceProvider

Broadcast::routes(['middleware' => ['auth:api']]);

Конфигурация аутентификации

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
        'hash' => false,
    ],
],

127.0.0.1:8000/broadcasting/auth Response при доступе без токена

{"message":"Unauthenticated."}

Laravel-Echo-Server

[4:50:17 PM] - Preparing authentication request to: http://127.0.0.1:8000
[4:50:17 PM] - Sending auth request to: http://127.0.0.1:8000/broadcasting/auth
[4:50:17 PM] - LtnbMInYDGa_QMMcAAAA authenticated for: private-user.1
[4:50:17 PM] - LtnbMInYDGa_QMMcAAAA joined channel: private-user.1

Итак, я предполагаю, что laravel-echo-server не возвращает false при ответе Unauthenticated

Любая помощь будет оценена


person Amir Parvez    schedule 14.07.2020    source источник


Ответы (1)


Хорошо, это было забавное решение. Я решил проверить, как laravel-echo-server запрашивает широковещательную передачу / аутентификацию и как он обрабатывает ответ на этот запрос.

Вы можете посмотреть здесь: https://github.com/tlaverdure/laravel-echo-server/blob/master/src/channels/private-channel.ts

Таким образом, laravel-echo-server возвращает true, если код ответа широковещательной передачи / аутентификации равен 200, и возвращает false, если код ответа не равен 200 или если произошла ошибка запроса.

Проблема здесь в том, что когда вы отправляете запрос на маршрут api laravel, обрабатываемый аутентификацией паспорта, он возвращает сообщение Unauthenticated, но без кода 401, из-за которого laravel-echo-server считает, что запрос был успешным, и позволяет пользователю присоединиться к канал.

Решение:

  1. Возврат кода 401 с ответом без проверки подлинности паспорта
  2. Промежуточное ПО для аутентификации канала

Возврат кода 401 с ответом без проверки подлинности паспорта

каталог проекта \ приложение \ Исключения Handler.php

...
use Illuminate\Auth\AuthenticationException;

...
public function render($request, Exception $exception)
{
    if($exception instanceof AuthenticationException){
        return response()->json(['message' => $exception->getMessage()], 401);
    }else{
        return response()->json(['message' => $exception->getMessage() ]);
    }

    return parent::render($request, $exception);
}

Промежуточное ПО для аутентификации канала

php artisan make: промежуточное ПО [имя]

каталог проекта \ приложение \ Http \ Middleware [имя] .php

use Closure;
use Illuminate\Support\Facades\Auth;

class SocketAuth
{

    public function handle($request, Closure $next)
    {
        $user = Auth::User();

        if($user !== null){
            if($request->channel_name == "private-user.".$user->id){
                return $next($request);
            }else{
                return response()->json(["message" => "Unauthenticated."], 401);
            }
        }

        return response()->json(["message" => "Unauthenticated."], 401);
    }
}

BroadcastServiceProvider

Broadcast::routes(["prefix" => "api", 'middleware' => ['auth:api', 'SocketAuth']]);

Зарегистрировать промежуточное ПО

каталог проекта \ приложение \ Http Kernel.php

protected $routeMiddleware = [
    ...
    'SocketAuth' => \App\Http\Middleware\SocketAuth::class,
];

Конфигурация Laravel-Echo-Server

"authEndpoint": "/api/broadcasting/auth",

Результат

  1. 401 без аутентификации - (Laravel-Echo-Server False) Вкл: [токен отсутствует в запросе со стороны клиента, запрошенный канал пользователя! == Запрос канала пользователя]

  2. Authenticated 200 - (Laravel-Echo-Server True) Вкл: [Токен присутствует в запросе со стороны клиента и запрошенном канале пользователя == Запрашиваемый канал пользователя]

Вы можете применить свою логику для аутентификации пользователей к каналам в промежуточном программном обеспечении.

person Amir Parvez    schedule 14.07.2020
comment
Проблема в том, что $ user = Auth :: User (); не работает. Я не получаю никаких данных - person Gias; 13.11.2020