在Symfony messenger异步消息处理程序上使用投票者/权限的最佳方式



我正在开发一个应用程序,该应用程序安装了Symfony Messenger组件来处理异步消息。消息的处理程序需要检查一些特定用户的权限,比如一个确定的用户是否应该收到一封包含信息的电子邮件,例如是否具有编辑权限。

为了实现这一点,我们使用Symfony投票者,但当我们没有任何用户登录到系统时,比如控制台命令和异步消息,这是非常令人讨厌的。最好的解决方案是什么?这是我的主要想法:

  • 强制a";登录";带有消息的安全上下文

    • Pro:一种无需额外服务即可检查权限的方法。选民就是服务
    • 缺点:当我有一个用户集合检查时,我应该做";安全上下文登录";动作多次。我觉得这很难
  • 设计一个域服务来处理它。

    • 优点:无需强制登录即可解决问题
    • 缺点:根据上下文(请求、控制台命令或异步队列(,重复代码或不同的方法来做相同的事情
  • 应该由投票者和域服务调用的服务

    • 缺点:我认为这会给更简单的问题增加复杂性

什么是最好的方法?除了前面的三点之外,还有什么想法吗?

非常感谢

我可能更喜欢在发送消息之前检查用户的权限,但让我们思考一下,如果不是合适的情况,我们该如何处理。

为了检查用户权限,您需要对用户进行身份验证。但在异步使用消息或执行控制台命令的情况下,这并不简单,因为您没有实际的用户。但是,您可以将用户id与消息一起传递给控制台命令。

让我分享一下我对Symfony Messenger简单解决方案的想法。在Symfony Messenger中,有一个Stamps的概念,它允许您向消息添加元数据。在我们的情况下,将用户id与消息一起传递会很有用,这样我们就可以在消息处理过程中对用户进行身份验证。

让我们创建一个自定义戳来保存用户id。这是一个简单的PHP类,因此不需要将其注册为服务。

<?php
namespace AppMessengerStamp;
use SymfonyComponentMessengerStampStampInterface;
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有中间件的概念,所以让我们创建一个中间件来处理工作人员发送的消息。它将检查消息是否包含AuthenticationStamp,并在用户当前未通过身份验证的情况下对用户进行身份验证。

<?php
namespace AppMessengerMiddleware;
use AppMessengerStampAuthenticationStamp;
use AppRepositoryUserRepositoryInterface;
use SymfonyComponentMessengerEnvelope;
use SymfonyComponentMessengerMiddlewareMiddlewareInterface;
use SymfonyComponentMessengerMiddlewareStackInterface;
use SymfonyComponentSecurityCoreAuthenticationTokenAnonymousToken;
use SymfonyComponentSecurityCoreAuthenticationTokenUsernamePasswordToken;
use SymfonyComponentSecurityCoreAuthenticationTokenStorageTokenStorageInterface;
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);
}
}

让我们将其注册为服务(或autowire(,并将其包含在信使配置定义中。

framework:
messenger:
buses:
messenger.bus.default:
middleware:
- 'AppMessengerMiddlewareAuthenticationMiddleware'

差不多了。现在你应该能够使用常规的方式来检查用户的权限,例如投票者。

至于控制台命令,我会选择一个身份验证服务,如果用户id被传递给命令,它会对用户进行身份验证。

最新更新