Laravel Echo - 允许访客连接到出席频道
Posted
技术标签:
【中文标题】Laravel Echo - 允许访客连接到出席频道【英文标题】:Laravel Echo - Allow guests to connect to presence channel 【发布时间】:2017-09-06 14:21:30 【问题描述】:我正在使用 laravel-echo-server 运行 Laravel Echo 来广播事件。
我有一个用户计数器频道,它显示应用程序上的所有用户。为此,我使用了一个存在渠道。这对于已登录的用户来说效果很好,但客人永远无法连接。
我在 BroadcastServiceProvider 中设置了以下内容:
Broadcast::channel('global', function ()
return ['name' => 'guest'];
);
据我所知,应该允许每个人作为“客人”进入。我猜在此之前我需要为这个频道禁用一些中间件或身份验证。
非常感谢任何有关让所有客户加入此展示频道的帮助!
【问题讨论】:
【参考方案1】:对于任何寻找此问题的答案的人。确实可以将客人授权到出席频道,您只需要用您自己的服务提供商覆盖 Broadcast::routes()。
例如,我的出席频道“全球”接受客人:
Route::post('/broadcasting/auth', function(Illuminate\Http\Request $req)
if($req->channel_name == 'presence-global')return 'global';
return abort(403);
);
这可以向各个方向扩展,或者可以继续将其他存在和私人频道传递给默认的 Broadcast::auth 方法
【讨论】:
然后,你如何加入房间?【参考方案2】:您可以使用factory(User::class)->make(...)
创建一个临时用户,并使用中间件对其进行身份验证以将其用作访客。
第 1 步:创建中间件
运行: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
中设置 AuthenticateGuest 中间件。
在app\Http\Kernel.php
:
protected $routeMiddleware = [
...
'authenticate-guest' => \App\Http\Middleware\AuthenticateGuest::class,
];
第二步:设置广播::频道路由
在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
【讨论】:
那么就跳过第 1 步,然后在第 2 步调整代码? 是的。我会更新我的答案以保持它更干净。感谢您的评论! 很好,只是想澄清一下,以便我可以尝试一下,并不是要吹毛求疵。 但是你连中间件都不用?它刚刚创建。您还需要在 BroadcastServiceProvider 的引导方法中注册它。否则它不会被调用。【参考方案3】:其他解决方案不适用于访客通道,这就是我最终得到的结果:
// 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】:您可以创建自己的身份验证守卫,它也很简单,但更复杂。
-
创建一个将实现可验证接口的类。
创建用户提供者。
创建一个新的 Guard。
在 AuthServiceProvider 中注册 Guard 和 UserProvider。
在config/auth.php中添加provider和guard
使用你的新警卫。
优势
您不必修改身份验证端点 您不必更改默认保护 你基于 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 方法中的 Identifier 属性始终是会话 ID,因此您也可以将其用作令牌。
3。创建新的警卫
<?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中添加provider
'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 的实例。
And that how it looks in the laravel-echo-server database
【讨论】:
【参考方案5】:在 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);
希望对某人有所帮助,
塞巴斯蒂安
【讨论】:
【参考方案6】:我的问题解决方案:
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); 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
);
【讨论】:
以上是关于Laravel Echo - 允许访客连接到出席频道的主要内容,如果未能解决你的问题,请参考以下文章