Laravel Echo - 允许访客连接到状态频道

时间:2017-04-11 09:23:43

标签: php laravel socket.io laravel-echo

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

我有一个用户计数器频道,可以显示应用中的所有用户。为此,我使用了在线频道。这适用于登录用户,但客人永远不会连接。

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

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

根据我的说法,应该允许所有人进入'客人'。我猜测在此之前有一些中间件或身份验证正在检查我需要为此频道禁用。

任何有关让所有客户加入此展示频道的帮助都将非常感谢!

6 个答案:

答案 0 :(得分:2)

对于寻找答案的人。确实可以将guest虚拟机授权到状态通道,您只需要使用自己的服务提供程序覆盖Broadcast :: routes()。

作为一个例子,我的在线频道'全球'接待客人:

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

这可以在各个方向扩展,或者可以继续将其他状态和私有通道传递到默认的Broadcast :: auth方法

答案 1 :(得分:2)

这可以很简单!

  1. 制作一个种子来创建名为" Guest"
  2. 的访客用户
  3. 使用\ Auth :: loginUsingId($ guestUserId);
  4. 制作一个中间件,以便在每个请求上对访客用户进行身份验证

    完成!

    现在您有一个经过身份验证的访客用户。

    如何编码

    第1步:创建播种机

    首先,您需要制作种子,以便在需要运行迁移时创建访客用户:

    php artisan make:seed UsersTableSeed

    在database / seeds / UsersTableSeeder.php中,您需要:

    public function run()
    {
        factory(User::class)->create([
            'name' => 'Guest'
        ]);
    }
    
      

    确保您将此种子作为数据库中的第一个用户,即ID为1

    第2步:创建中间件

    在此步骤中,您需要创建将在每个请求中对访客用户进行身份验证的中间件。只是做:

    php artisan make:middleware AuthenticateGuest

    在app / Http / Middleware / AuthenticateGuest.php中你需要:

    public function handle($request, Closure $next)
    {
        Auth::loginUsingId(1); // Guest
    
        return $next($request);
    }
    

    现在需要在Kernel.php中设置AuthenticateGuest中间件。

    在app \ Http \ Kernel.php中你需要:

    protected $middleware = [
        ...
        \App\Http\Middleware\AuthenticateGuest::class, // if you need it for all the routes
    ];
    
    protected $middlewareGroups = [
        'web' => [
            ...
            \App\Http\Middleware\AuthenticateGuest::class, // if you need it for web routes
        ]
        'api' => [
            ...
            \App\Http\Middleware\AuthenticateGuest::class, // if you need it for api routes
        ],
    ];
    
    protected $routeMiddleware = [
        ...
        'authenticate-guest' => \App\Http\Middleware\AuthenticateGuest::class, // if you need to use this custom middleware
    ];
    

    步骤3:设置广播::频道路径

    在routes / channels.php中,你需要:

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

    如果您需要自定义渠道,请考虑使用' App.Room。{id}'验证访客用户到指定的房间。

    重要更新

    根据PresenceChannel规则,您无法为所有连接使用相同的用户ID。通过这种方式,您将看不到其他用户连接,并且Laravel Echo joining()方法也不会被触发。

    要解决此问题,您需要删除播种机(现在它将无用)。

    现在您只需将中间件更改为:

    Auth::login(factory(User::class)->make([
        'id' => (int) str_replace('.', '', microtime(true))
    ]));
    

答案 2 :(得分: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);
    }
}

希望能帮助某人

塞巴斯蒂安

答案 3 :(得分:0)

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

// 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
    ];
});



答案 4 :(得分:0)

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

  1. 创建一个将实现Authenticable Interface的类。
  2. 创建UserProvider。
  3. 创建一个新的Guard。
  4. 在AuthServiceProvider中注册Guard和UserProvider。
  5. 在config / auth.php中添加提供程序和保护程序
  6. 使用新警卫。

优势

  • 您不必修改身份验证端点
  • 您不必更改默认后卫
  • 您基于Laravel Auth系统
  • 在浏览器中保持对多个标签的支持
  • 可以同时与Web Guard一起使用
  • 保留使用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。创建UserProvider

<?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;
    }
}

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

请注意,您可以同时使用“网络”作为防护(“网络”应位于“访客”之前)。它可以让您找出谁是访客以及谁是登录用户-您只需在频道回调中检查Authenticable实例即可。

And that how it looks in the laravel-echo-server database

答案 5 :(得分: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');
}

Kernel.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); Channel Classes

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