使用 java 客户端验证 laravel 中的私人频道时出现问题

Problem with authenticating private channels in laravel with java client

我想从服务器(使用 laravel)向客户端(使用 java)发送广播消息。

我在用什么

  1. Pusher 作为广播驱动程序。
  2. laravel 用于 api 身份验证的护照。

我在服务器端做了什么

  1. 我已经在 .env 文件中配置了我的 Pusher 凭据。
  2. 取消注释 config/app.php 文件中的 App\Providers\BroadcastServiceProvider::class 行。
  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 class 中,我将以下内容添加到 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. 通过 运行 php artisan make:event AdvertisementAdded 创建了一个事件 AdvertisementAdded,添加了 implements ShouldBroadcast,然后将以下内容添加到其 broadcastOn() 方法中:
return new PrivateChannel('device.'.$this->device_id);

我在客户端做了什么

因为我现在只是在测试,所以我通过邮递员

发送登录请求得到了我的access_tokendevice_id

我将该 accessToken 复制到我的 java 客户端并将其作为 String 存储在 accessToken 变量中,代码如下:

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');
    });
});

这是邮递员对该路由的测试(使用与上述相同的访问令牌):

这里是对来自邮递员的 api/broadcasting/auth 路由的测试(使用与上述相同的访问令牌):

有什么问题吗?为什么 auth:api 中间件下的所有 api 路由都能正常工作,但 api/broadcasting/auth 路由不能正常工作??

备注

我尝试使用 public 个频道没有问题。

找了一整天,终于解决了

授权通道时发生错误,而不是使用 auth:api 中间件验证请求时发生错误。

我在 routes/channels.php 中的私人频道授权函数总是 returns 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;
});

上面的授权函数总是return false,因为$device->id(类型int)和$device_id(类型类型为 string).

所以,为了解决这个问题,我将它们都转换为int,然后检查是否相等。

这是我用来解决问题的代码:

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