java - 使用java客户端在laravel中验证私有(private) channel 的问题

标签 java php laravel pusher laravel-passport

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

我正在使用的东西

  1. Pusher作为广播驱动程序。
  2. 用于 API 身份验证的 Laravel Passport。

我在服务器端做了什么

  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,
    ],
],
  • App\Providers\BroadcastServiceProvider 类中,我将以下内容添加到 boot() 函数:
  • Broadcast::routes(['prefix' => 'api', 'middleware' => 'auth:api']);
    
  • routes/channels.php 中,我添加了以下内容:
  • Broadcast::channel('device.{device_id}', function ($device, $device_id) {
        return $device->id === $device_id;
    });
    
  • 通过运行 php artisan make:event AdvertisingAdded 创建了一个事件 AdvertisementAdded,添加了 implements ShouldBroadcast,然后将以下内容添加到其 broadcastOn() 方法中:
  • return new PrivateChannel('device.'.$this->device_id);
    

    我在客户端做了什么

    因为我现在刚刚测试,所以我通过 postman 发送登录请求获得了我的 access_tokendevice_id

    Getting access_token from postman

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

    这是 postman 对该路由的测试(具有与上面相同的访问 token ):

    Successful api authentication test on postman

    这是对来自 postman 的 api/broadcasting/auth 路由的测试(具有与上面相同的访问 token ):

    Failed api authentication on api/broadcasting/auth route

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

    注意

    我尝试使用公共(public) channel ,没有出现任何问题。

    最佳答案

    经过一整天的寻找,终于解决了。

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

    routes/channels.php 中的私有(private) channel 授权函数始终返回 false,这意味着它将拒绝对 private-device.{device_id} channel 的所有订阅请求:

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

    由于 $device->id(int 类型)和 $device_id(string 类型)之间的类型不相等,上述授权函数始终返回 false

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

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

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

    关于java - 使用java客户端在laravel中验证私有(private) channel 的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56363780/

    相关文章:

    java - 使用 N :1 relationship 将同一列保存在 JPA secondaryTable 中

    java - Android Studio .jar lib 中添加了一些奇怪的东西

    php - 使用php从mysql编码或解码字符串

    php - Laravel 4 在 Controller 中注入(inject) Eloquent 模型

    mysql - 移动小数 sql 或除法

    Laravel - 访问 .env 变量

    java - 在 Raspberry PI 2 上使用 java 和 Pi4J 控制伺服系统时出现问题

    java - 为 setDefaultCloseOperation 创建自定义操作?

    php - file_get_contents() 的问题

    php循环遍历多个数组