Проблема с аутентификацией частных каналов в laravel с помощью Java-клиента - PullRequest
1 голос
/ 29 мая 2019

Я хочу отправлять широковещательные сообщения с сервера (используя laravel) клиентам (используя java).

Что я использую

  1. Pusher в качестве драйвера boradcast.
  2. паспорт laravel для аутентификации API.

Что я сделал на стороне сервера

  1. Я настроил свои учетные данные Pusher в .env файле.
  2. Не комментированная App\Providers\BroadcastServiceProvider::class строка в config/app.php файле.
  3. В config/auth.php файле я добавил следующее:
'guards' => [
     'web' => [
         'driver' => 'session',
         'provider' => 'users',
     ],

     'devices' => [
         'driver' => 'session',
         'provider' => 'devices',
     ],

     'api' => [
         'driver' => 'passport',
         'provider' => 'devices',
     ],
 ],

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\User::class,
    ],

     // using devices table to authenticate over api guard
    'devices' => [
        'driver' => 'eloquent',
        'model' => App\Device::class,
    ],
],
  1. В App\Providers\BroadcastServiceProvider классе я добавил следующее к boot() функции:
Broadcast::routes(['prefix' => 'api', 'middleware' => 'auth:api']);
  1. В routes/channels.php я добавил следующее:
Broadcast::channel('device.{device_id}', function ($device, $device_id) {
    return $device->id === $device_id;
});
  1. Создал событие AdvertisementAdded, запустив php artisan make:event AdvertisementAdded, добавил implements ShouldBroadcast и добавил следующее к его broadcastOn() методу:
return new PrivateChannel('device.'.$this->device_id);

Что я сделал на стороне клиента

Поскольку я сейчас только тестирую, я получил access_token и device_id, отправив запрос на вход от почтальона

Getting access_token from postman

Я скопировал этот accessToken в мой Java-клиент и сохранил его в переменной accessToken как String, вот код:

String accessToken = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImY3ZTVlMTAzZWE3MzJjMTI5NzY1YTliMmMzOTM0N2ZhOGE4OTU5MjRjNDA5ZjgyOTA4ZDg5NTFjZTBkOGZlNTA2M2M1YTI1MDBlOTdhZDdiIn0.eyJhdWQiOiIxIiwianRpIjoiZjdlNWUxMDNlYTczMmMxMjk3NjVhOWIyYzM5MzQ3ZmE4YTg5NTkyNGM0MDlmODI5MDhkODk1MWNlMGQ4ZmU1MDYzYzVhMjUwMGU5N2FkN2IiLCJpYXQiOjE1NTkwOTYyNDgsIm5iZiI6MTU1OTA5NjI0OCwiZXhwIjoxNTkwNzE4NjQ3LCJzdWIiOiI3Iiwic2NvcGVzIjpbXX0.FKeE9Z-wv2yUNQPl-qsbu9baYGTdbQ6DuzaI1R8azR6l1CIP9uRI4hCaoWvgx0GXWWLPRNhfQl-YD3KP2YOraW16-h4ie_95B9VQrpFxXnlqKojsfh1xSrSNSl5HncslMWQPVjoesBpM5y_cpG19PGgu-SWo0W6s9Fiz_Nm70oyyZB9mSqU8PVQvAOSNr6TMR0aC3iMLFfkyZkTSwj8EoRyD2LGW6v4PFriqx8JLbZASCOiUYBlYnunWrTFDOAenZcoa5Sw7u7kbSvYehjDKRwKjQM6zmPfi0A3Mp0CHjHE599OXb-NG2IMH-wmlT0vEZjP2U97hxmsNW1RtHNXWaRKFL9T-WVmZbJf3fH5hXqTv495L3MQfq_m5YFHyc5NuIqK4K4xMJB956a33ICnH8DmvPmJgderNAhqEX1JHUAsR63K7xbZxRBDS8OlQYcEf-_v75X0kT1s067enSvI8Vs212AVnI6k0FmgQNM8DfJUq6YduD0m2F2ZWpKPrwdd6PdW5ZlZTEv-D8dYIEQ_CwOWohNoENATmTqxDpPBxK5c723MEt8S7Sa9MEGAo56HW3-9pbazbEdY1GqPWKVkov7K_6eBFcWsV67AgJpoKFt6RiBfRvokgiH96WG89qBB_Ucpm8uBahX93FaOXhVLW0VjJH2LQKrGw0bb5LS8Ql5o";
String deviceId = "7";

Map<String, String> authHeaders = new HashMap();

authHeaders.put("Authorization", accessToken);

HttpAuthorizer authorizer = new HttpAuthorizer("http://localhost:8000/api/broadcasting/auth");
authorizer.setHeaders(authHeaders);
PusherOptions options = new PusherOptions();
options.setAuthorizer(authorizer).setCluster(PUSHER_CLUSTER);
Pusher pusher = new Pusher(PUSHER_APP_KEY, options);

pusher.subscribePrivate("private-device." + deviceId, new PrivateChannelEventListener() {
    @Override
    public void onEvent(String channelName, String eventName, final String data) {
        System.out.println(String.format("Received event on channel [%s]", channelName));
    }

    @Override
    public void onSubscriptionSucceeded(String string) {
        System.out.println(String.format("Subscribed to channel [%s]", string));
    }

    @Override
    public void onAuthenticationFailure(String string, Exception excptn) {
        System.out.println(string);
    }
});

pusher.connect(new ConnectionEventListener() {
    @Override
    public void onConnectionStateChange(ConnectionStateChange change) {
        System.out.println("State changed to " + change.getCurrentState() +
                           " from " + change.getPreviousState());
    }

    @Override
    public void onError(String message, String code, Exception e) {
        System.out.println("There was a problem connecting!");
    }
});

// Keeping main thread alive
while (true) {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

При запуске приведенного выше кода на консоль выводится следующее:

State changed to CONNECTING from DISCONNECTED
State changed to CONNECTED from CONNECTING
java.io.IOException: Server returned HTTP response code: 403 for URL: http://localhost:8000/api/broadcasting/auth

Я уверен, что auth:api промежуточное ПО работает, как я ожидаю, на других запросах.

Вот фрагмент из моего routes/api.php:

Route::middleware('auth:api')->group(function () {
    Route::prefix('advertisements')->group(function () {
        Route::get('/request', 'AdvertisementsController@getDeviceAdvertisements')
            ->name('advertisements.getDeviceAdvertisements');
    });
});

А вот тест на этот маршрут от почтальона (с тем же маркером доступа, что и выше):

Successful api authentication test on postman

А вот тест на api/broadcasting/auth маршрут от почтальона (с тем же маркером доступа, что и выше):

Failed api authentication on api/broadcasting/auth route

В чем проблема? Почему все маршруты API под промежуточным программным обеспечением auth:api работают нормально, а не api/broadcasting/auth route ??

Примечание

Я попытался работать с публичными каналами без проблем.

1 Ответ

0 голосов
/ 30 мая 2019

После целого дня поиска, наконец-то все решено.

Ошибка возникает при авторизации канала, а не при аутентификации запроса с использованием auth:api middleware.

Моя функция авторизации частного каналав routes/channels.php всегда возвращает false, что означает, что он отклонит все запросы на подписку на private-device.{device_id} канал:

Broadcast::channel('device.{device_id}', function ($device, $device_id) {
    // this always return false, because of inequality of types
    return $device->id === $device_id;
});

Функция авторизации выше всегда возвращает false из-за неравенства типов между $device->id (который имеет тип int) и $device_id (который имеет тип string).

Итак, чтобы решить проблему, я приведу их обоих к int и затем проверил на наличиеравенство.

Вот код, который я использовал для решения проблемы:

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