9

我正在使用 laravel-echo-server 运行 Laravel Echo 来广播事件。

我有一个用户计数器频道,显示应用程序上的所有用户。为此,我使用了一个存在渠道。这对于已登录的用户来说效果很好,但客人永远无法连接。

我在 BroadcastServiceProvider 中设置了以下内容:

Broadcast::channel('global', function () { return ['name' => 'guest']; });

据我所知,应该让每个人都作为“客人”。我猜在此之前我需要为这个频道禁用一些中间件或身份验证。

任何有关让所有客户加入此存在渠道的帮助将不胜感激!

4

6 回答 6

3

您可以使用中间件创建一个临时用户factory(User::class)->make(...)并对其进行身份验证,以将其用作访客。

第一步:创建中间件

跑:php artisan make:middleware AuthenticateGuest

app/Http/Middleware/AuthenticateGuest.php

public function handle($request, Closure $next)
{
    Auth::login(factory(User::class)->make([
        'id' => (int) str_replace('.', '', microtime(true))
    ]));

    return $next($request);
}

现在在Kernel.php.

app\Http\Kernel.php

protected $routeMiddleware = [
    ...
    'authenticate-guest' => \App\Http\Middleware\AuthenticateGuest::class,
];

第 2 步:设置 Broadcast::channel 路由

routes/channels.php

Broadcast::channel('chatroom', function ($user) {
    return $user; // here will return the guest user object
});

更多信息:https ://laravel.com/docs/8.x/broadcasting#authorizing-presence-channels

于 2018-01-08T11:23:27.403 回答
3

对于任何正在寻找答案的人。确实可以将客人身份验证到存在频道,您只需要用您自己的服务提供商覆盖 Broadcast::routes() 即可。

例如,我的出席频道“全球”接受客人:

Route::post('/broadcasting/auth', function(Illuminate\Http\Request $req) { if($req->channel_name == 'presence-global'){return 'global';} return abort(403); });

这可以向各个方向扩展,或者可以继续将其他存在和私人频道传递给默认的 Broadcast::auth 方法

于 2017-04-12T10:45:41.930 回答
2

其他解决方案不适用于访客存在渠道,这就是我最终得到的结果:

// routes/channels.php

<?php
use Illuminate\Auth\GenericUser;

/*
|--------------------------------------------------------------------------
| Broadcast Channels
|--------------------------------------------------------------------------
|
| Here you may register all of the event broadcasting channels that your
| application supports. The given channel authorization callbacks are
| used to check if an authenticated user can listen to the channel.
|
*/

Route::post('/custom/broadcast/auth/route', function () {
    $user = new GenericUser(['id' => microtime()]);

    request()->setUserResolver(function () use ($user) {
        return $user;
    });

    return Broadcast::auth(request());
});

Broadcast::channel('online.{uuid}', function ($user, $uuid) {
    return [
        'id' => $user->id,
        'uuid' => $uuid
    ];
});



于 2019-03-27T14:05:43.050 回答
1

您可以创建自己的身份验证保护,它也非常简单但更复杂。

  1. 创建一个将实现可验证接口的类。
  2. 创建用户提供者。
  3. 创建一个新的守卫。
  4. 在 AuthServiceProvider 中注册 Guard 和 UserProvider。
  5. 在 config/auth.php 中添加提供者和守卫
  6. 使用你的新后卫。

优点

  • 您不必修改身份验证端点
  • 您不必更改默认守卫
  • 你基于 Laravel Auth 系统
  • 保持对浏览器中多个选项卡的支持
  • 可与网卫同时使用
  • 保留使用 PresenceChannel 的所有优势

缺点

  • 很多代码

所以,

1. 创建一个将实现 Authenticable 接口的新类。

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use JsonSerializable;

/**
 * @property string $id
 * @property string $name
 */
class Session implements Authenticatable, Jsonable, Arrayable, JsonSerializable
{

    private $id;

    private $attributes = [];

    public function __construct($id)
    {
        $this->id = $id;
        $this->name = "Guest";
    }

    /**
     * Get the name of the unique identifier for the user.
     *
     * @return string
     */
    public function getAuthIdentifierName()
    {
        return 'id';
    }

    /**
     * Get the unique identifier for the user.
     *
     * @return mixed
     */
    public function getAuthIdentifier()
    {
        return $this->{$this->getAuthIdentifierName()};
    }

    /**
     * Get the password for the user.
     *
     * @return string
     */
    public function getAuthPassword()
    {
        return "";
    }

    /**
     * Get the token value for the "remember me" session.
     *
     * @return string
     */
    public function getRememberToken()
    {
        return $this->{$this->getAuthIdentifierName()};
    }

    /**
     * Set the token value for the "remember me" session.
     *
     * @param  string $value
     * @return void
     */
    public function setRememberToken($value)
    {
        $this->{$this->getRememberToken()} = $value;
    }

    /**
     * Get the column name for the "remember me" token.
     *
     * @return string
     */
    public function getRememberTokenName()
    {
        return "token";
    }

    public function __get($name)
    {
        return $this->attributes[$name];
    }

    public function __set($name, $value)
    {
        $this->attributes[$name] = $value;
    }

    /**
     * Convert the object to its JSON representation.
     *
     * @param  int $options
     * @return string
     */
    public function toJson($options = 0)
    {
        return json_encode($this);
    }

    /**
     * Get the instance as an array.
     *
     * @return array
     */
    public function toArray()
    {
        return $this->attributes;
    }

    /**
     * Specify data which should be serialized to JSON
     * @link https://php.net/manual/en/jsonserializable.jsonserialize.php
     * @return mixed data which can be serialized by <b>json_encode</b>,
     * which is a value of any type other than a resource.
     * @since 5.4.0
     */
    public function jsonSerialize()
    {
        return $this->attributes;
    }
}

根据需要修改它,但你不应该序列化 $id 属性

2.创建用户提供者

<?php namespace App\Extensions;

use App\Models\Session;
use Illuminate\Cache\Repository;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Support\Fluent;
use Illuminate\Support\Str;

class SessionUserProvider implements UserProvider
{

    private $store;

    /**
     * SessionUserProvider constructor.
     * @param Repository $store
     */
    public function __construct(Repository $store)
    {
        $this->store = $store;
    }


    /**
     * Retrieve a user by their unique identifier.
     *
     * @param  mixed $identifier
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveById($identifier)
    {
        return new Session(
            $this->getUniqueTokenForSession($identifier)
        );
    }

    /**
     * Retrieve a user by their unique identifier and "remember me" token.
     *
     * @param  mixed $identifier
     * @param  string $token
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByToken($identifier, $token)
    {
        return null;
    }

    /**
     * Update the "remember me" token for the given user in storage.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable $user
     * @param  string $token
     * @return void
     */
    public function updateRememberToken(Authenticatable $user, $token)
    {
        return;
    }

    /**
     * Retrieve a user by the given credentials.
     *
     * @param  array $credentials
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function retrieveByCredentials(array $credentials)
    {
        return null;
    }

    private function unpack($data)
    {
        return json_decode($data);
    }

    private function getUniqueTokenForSession($id)
    {
        return $this->retrieveCacheDataForSession($id)
            ->get('uuid');
    }

    private function retrieveCacheDataForSession($id)
    {
        $fluent = new Fluent(
            $this->unpack(
                $this->store->has($id) ? $this->store->get($id) : "[]"
            )
        );

        if(!$fluent->__isset('uuid')) {
            $fluent->__set('uuid', Str::random(128));
        }

        $this->store->put($id, $fluent->toJson(), 60 * 60 * 60);

        return $fluent;

    }

    /**
     * Validate a user against the given credentials.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable $user
     * @param  array $credentials
     * @return bool
     */
    public function validateCredentials(Authenticatable $user, array $credentials)
    {
        return null;
    }
}

如果您使用广播, retrieveById方法中的标识符属性始终是会话 ID,因此您也可以将其用作令牌。

3.创建新的Guard

<?php namespace App\Services\Auth;

use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Http\Request;

class GuestGuard implements Guard
{

    private $user;
    protected $request;
    protected $provider;

    /**
     * GuestGuard constructor.
     * @param UserProvider $provider
     * @param Request $request
     */
    public function __construct(UserProvider $provider, Request $request)
    {
        $this->provider = $provider;
        $this->request = $request;
    }


    /**
     * Determine if the current user is authenticated.
     *
     * @return bool
     */
    public function check()
    {
        return !is_null($this->user);
    }
    
    /**
     * Determine if the current user is a guest.
     *
     * @return bool
     */
    public function guest()
    {
        return !$this->check();
    }

    /**
     * Get the currently authenticated user.
     *
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function user()
    {
        if($this->check()) {
            return $this->user;
        }

        $this->setUser(
            $this->provider->retrieveById(
                $this->request->session()->getId()
            )
        );

        return $this->user;
    }

    /**
     * Get the ID for the currently authenticated user.
     *
     * @return int|null
     */
    public function id()
    {
        return !is_null($this->user) ? $this->user->id : null;
    }

    /**
     * Validate a user's credentials.
     *
     * @param  array $credentials
     * @return bool
     */
    public function validate(array $credentials = [])
    {
        return false;
    }

    /**
     * Set the current user.
     *
     * @param  \Illuminate\Contracts\Auth\Authenticatable $user
     * @return void
     */
    public function setUser(Authenticatable $user)
    {
        $this->user = $user;
    }
}

用户方法中,您将会话 ID 作为标识符传递,仅使用广播此方法是必要的。

4、在AuthServiceProvider中注册Guard和UserProvider。

// app/Providers/AuthServiceProvider.php

   /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        Auth::provider('sessions', function (Application $app) {
            return new SessionUserProvider(
                $app->make('cache.store')
            );
        });

        Auth::extend('guest', function (Application $app, $name, array $config) {
            return new GuestGuard(Auth::createUserProvider($config['provider']), $app->make('request'));
        });
    }

5.1 在 config/auth.php 中添加提供者

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
        
        // New
        'sessions' => [
         'driver' => 'sessions',
         'model' => App\Models\Session::class,
        ],
    ],

5.2 在 config/auth.php 中添加保护

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

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

        // New
        'guest' => [
            'driver' => 'guest',
            'provider' => 'sessions'
        ]
    ],

6.使用你的新后卫

// routes/channels.php

Broadcast::channel('chat.{id}', function (Authenticatable $user){
    return $user;
}, ['guards' => ['guest']]);

请注意,您可以同时使用 'web' 作为守卫('web' 应该在 'guest' 之前)。它允许您找出谁是访客以及谁是登录用户 - 您可以在通道回调中检查 Authenticable 的实例。

以及它在 laravel-echo-server 数据库中的外观

于 2019-06-22T08:06:33.527 回答
0

我的问题解决方案:

BroadcastServiceProvider.php (~/app/Providers/)

public function boot()
{
    if (request()->hasHeader('V-Auth')) { /* Virtual client. */
        Broadcast::routes(['middleware' => 'client_chat.broadcast.auth']);
    } else {
        Broadcast::routes();
    }

    require base_path('routes/channels.php');
}

内核.php (~/app/Http/)

protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    ...
    'client_chat.broadcast.auth' => \App\Http\Middleware\ClientChatBroadcasting::class,
];

ClientChatBroadcasting.php (~/app/Http/Middleware/)

public function handle($request, Closure $next)
{
    if (/** your condition **/) {
        $fakeUser = new User;
        $fakeUser->virtual_client = true;
        $fakeUser->id = /** whatever you want **/;
        $fakeUser->name = '[virtual_client]';
        $fakeUser->asdasdasdasdasd = 'asdasdasdasdasd';

        $request->merge(['user' => $fakeUser]);
        $request->setUserResolver(function () use ($fakeUser) {
            return $fakeUser;
        });
    }

    return $next($request);
}

ChatChannel.php (~/app/Broadcasting/Chat/)

Broadcast::channel('chat.{chatId}', ChatChannel::class); 频道类

public function join($member/**($fakeUser)**/, $chatId)
{
    $memberData = [/** your data **/];

    /* If there is no member data (null), then there will be an authentication error. */
    return $memberData;
}

[放在你的 js 文件中,你想连接到广播的地方]

this.Echo = new Echo({
        broadcaster: 'socket.io',
        host: /** your host **/,
        reconnectionAttempts: 60,
        encrypted: true,
        auth: {
            headers: {
                'V-Auth': true,
                'Access-Token': accessToken,
                'Virtual-Id': virtualId,
                'Chat-Id': chatId
            }
        }
    });
于 2019-07-30T10:32:24.903 回答
0

在 Renan Coelho 的帮助下,我得到了它的工作。对我来说缺少的部分是使用以下内容覆盖 Broadcast::routes() 方法:

Route::post('/broadcasting/auth', function (Illuminate\Http\Request $req) {
    return Broadcast::auth($req);
});

Route::post('/broadcasting/auth'...实际上是通过“Broadcast::routes()”方法添加的路由。这就是我们在这里覆盖它的原因。php artisan route:list您可以通过在终端中输入来查看活动路线。

然后,Renan Coelho 已经说过,我必须添加一个自定义中间件(AuthenticateGuest),为我创建一个随机用户。(这是 hacky 部分)并将其添加到 kernel.php 中的 $middleware 数组中:

protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
        \Barryvdh\Cors\HandleCors::class,

        \App\Http\Middleware\AuthenticateGuest::class
    ];

AuthenticateGuest 中间件如下所示:

<?php

namespace App\Http\Middleware;

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

class AuthenticateGuest
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        Auth::login(factory(User::class)->make([
            'id' => (int)str_replace('.', '', microtime(true))
        ]));

        return $next($request);
    }
}

希望对某人有所帮助,

塞巴斯蒂安

于 2018-09-24T09:27:20.113 回答