在 Symfony Messenger 异步消息处理程序上使用 voters/permissions 的最佳方式?
Best way to use voters/permissions on Symfony messenger async message handler?
我正在开发一个安装了 Symfony Messenger 组件来处理异步消息的应用程序。消息的处理程序需要检查某些特定用户的某些权限,例如,如果一个确定的用户如果具有编辑权限,是否应该收到一封包含信息的电子邮件。
为了实现这一点,我们使用了 Symfony 选民,但是当我们没有任何用户登录系统时,就像在控制台命令和异步消息中一样,这非常烦人。最好的解决方案是什么?这是我的主要想法:
使用消息的安全上下文强制“登录”
- 专业版:一种无需额外服务即可检查权限的方法。选民就是服务。
- 缺点:当我有一组用户检查时,我应该多次执行“安全上下文登录”操作。我觉得很难。
设计域服务来处理该问题。
- 优点:无需强制登录即可解决问题
- 缺点:根据上下文(请求、控制台命令或异步队列),重复的代码或不同的方法来做同样的事情
选民和域服务应该调用的服务
- 缺点:我认为这会增加更简单问题的复杂性
最好的方法是什么?除了前三点之外还有什么想法吗?
非常感谢
我可能更愿意在发送消息之前检查用户的权限,但让我们想想如果情况不合适我们该如何处理。
为了检查用户权限,您需要对用户进行身份验证。但是,如果您异步使用消息或执行控制台命令,这并不简单,因为您没有实际用户。但是,您可以将用户 ID 与您的消息或控制台命令一起传递。
让我分享一下我对 Symfony Messenger. In the Symfony Messenger, there is a concept of Stamps 的简单解决方案的想法,它允许您将元数据添加到消息中。在我们的例子中,通过消息传递用户 ID 会很有用,这样我们就可以在消息处理过程中对用户进行身份验证。
让我们创建一个自定义图章来保存用户 ID。这是一个简单的PHPclass,因此无需将其注册为服务。
<?php
namespace App\Messenger\Stamp;
use Symfony\Component\Messenger\Stamp\StampInterface;
class AuthenticationStamp implements StampInterface
{
private $userId;
public function __construct(string $userId)
{
$this->userId = $userId;
}
public function getUserId(): string
{
return $this->userId;
}
}
现在我们可以将图章添加到消息中了。
$message = new SampleMessage($payload);
$this->messageBus->dispatch(
(new Envelope($message))
->with(new AuthenticationStamp($userId))
);
我们需要接收和处理戳记才能对用户进行身份验证。 Symfony Messenger 有一个 Middlewares 的概念,所以让我们创建一个来处理 stamp 当我们收到 worker 的消息时。它将检查消息是否包含 AuthenticationStamp 并在用户当前未通过身份验证时对用户进行身份验证。
<?php
namespace App\Messenger\Middleware;
use App\Messenger\Stamp\AuthenticationStamp;
use App\Repository\UserRepositoryInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class AuthenticationMiddleware implements MiddlewareInterface
{
private $tokenStorage;
private $userRepository;
public function __construct(TokenStorageInterface $tokenStorage, UserRepositoryInterface $userRepository)
{
$this->tokenStorage = $tokenStorage;
$this->userRepository = $userRepository;
}
public function handle(Envelope $envelope, StackInterface $stack): Envelope
{
/** @var AuthenticationStamp|null $authenticationStamp */
if ($authenticationStamp = $envelope->last(AuthenticationStamp::class)) {
$userId = $authenticationStamp->getUserId();
$token = $this->tokenStorage->getToken();
if (null === $token || $token instanceof AnonymousToken) {
$user = $this->userRepository->find($userId);
if ($user) {
$this->tokenStorage->setToken(new UsernamePasswordToken(
$user,
null,
'provider',
$user->getRoles())
);
}
}
}
return $stack->next()->handle($envelope, $stack);
}
}
让我们将其注册为服务(或自动装配)并包含到 Messenger 配置定义中。
framework:
messenger:
buses:
messenger.bus.default:
middleware:
- 'App\Messenger\Middleware\AuthenticationMiddleware'
差不多就这些了。现在您应该能够使用常规方式检查用户的权限,例如选民。
至于控制台命令,我会选择身份验证服务,如果将用户 ID 传递给命令,它将对用户进行身份验证。
我正在开发一个安装了 Symfony Messenger 组件来处理异步消息的应用程序。消息的处理程序需要检查某些特定用户的某些权限,例如,如果一个确定的用户如果具有编辑权限,是否应该收到一封包含信息的电子邮件。
为了实现这一点,我们使用了 Symfony 选民,但是当我们没有任何用户登录系统时,就像在控制台命令和异步消息中一样,这非常烦人。最好的解决方案是什么?这是我的主要想法:
使用消息的安全上下文强制“登录”
- 专业版:一种无需额外服务即可检查权限的方法。选民就是服务。
- 缺点:当我有一组用户检查时,我应该多次执行“安全上下文登录”操作。我觉得很难。
设计域服务来处理该问题。
- 优点:无需强制登录即可解决问题
- 缺点:根据上下文(请求、控制台命令或异步队列),重复的代码或不同的方法来做同样的事情
选民和域服务应该调用的服务
- 缺点:我认为这会增加更简单问题的复杂性
最好的方法是什么?除了前三点之外还有什么想法吗?
非常感谢
我可能更愿意在发送消息之前检查用户的权限,但让我们想想如果情况不合适我们该如何处理。
为了检查用户权限,您需要对用户进行身份验证。但是,如果您异步使用消息或执行控制台命令,这并不简单,因为您没有实际用户。但是,您可以将用户 ID 与您的消息或控制台命令一起传递。
让我分享一下我对 Symfony Messenger. In the Symfony Messenger, there is a concept of Stamps 的简单解决方案的想法,它允许您将元数据添加到消息中。在我们的例子中,通过消息传递用户 ID 会很有用,这样我们就可以在消息处理过程中对用户进行身份验证。
让我们创建一个自定义图章来保存用户 ID。这是一个简单的PHPclass,因此无需将其注册为服务。
<?php
namespace App\Messenger\Stamp;
use Symfony\Component\Messenger\Stamp\StampInterface;
class AuthenticationStamp implements StampInterface
{
private $userId;
public function __construct(string $userId)
{
$this->userId = $userId;
}
public function getUserId(): string
{
return $this->userId;
}
}
现在我们可以将图章添加到消息中了。
$message = new SampleMessage($payload);
$this->messageBus->dispatch(
(new Envelope($message))
->with(new AuthenticationStamp($userId))
);
我们需要接收和处理戳记才能对用户进行身份验证。 Symfony Messenger 有一个 Middlewares 的概念,所以让我们创建一个来处理 stamp 当我们收到 worker 的消息时。它将检查消息是否包含 AuthenticationStamp 并在用户当前未通过身份验证时对用户进行身份验证。
<?php
namespace App\Messenger\Middleware;
use App\Messenger\Stamp\AuthenticationStamp;
use App\Repository\UserRepositoryInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class AuthenticationMiddleware implements MiddlewareInterface
{
private $tokenStorage;
private $userRepository;
public function __construct(TokenStorageInterface $tokenStorage, UserRepositoryInterface $userRepository)
{
$this->tokenStorage = $tokenStorage;
$this->userRepository = $userRepository;
}
public function handle(Envelope $envelope, StackInterface $stack): Envelope
{
/** @var AuthenticationStamp|null $authenticationStamp */
if ($authenticationStamp = $envelope->last(AuthenticationStamp::class)) {
$userId = $authenticationStamp->getUserId();
$token = $this->tokenStorage->getToken();
if (null === $token || $token instanceof AnonymousToken) {
$user = $this->userRepository->find($userId);
if ($user) {
$this->tokenStorage->setToken(new UsernamePasswordToken(
$user,
null,
'provider',
$user->getRoles())
);
}
}
}
return $stack->next()->handle($envelope, $stack);
}
}
让我们将其注册为服务(或自动装配)并包含到 Messenger 配置定义中。
framework:
messenger:
buses:
messenger.bus.default:
middleware:
- 'App\Messenger\Middleware\AuthenticationMiddleware'
差不多就这些了。现在您应该能够使用常规方式检查用户的权限,例如选民。
至于控制台命令,我会选择身份验证服务,如果将用户 ID 传递给命令,它将对用户进行身份验证。