在 Symfony Messenger 异步消息处理程序上使用选民/权限的最佳方式? [关闭]

Posted

技术标签:

【中文标题】在 Symfony Messenger 异步消息处理程序上使用选民/权限的最佳方式? [关闭]【英文标题】:Best way to use voters/permissions on Symfony messenger async message handler? [closed] 【发布时间】:2021-01-22 04:04:30 【问题描述】:

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

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

使用消息的安全上下文强制“登录”

专业版:一种无需额外服务即可检查权限的方法。选民就是服务。 缺点:当我有一组用户检查时,我应该多次执行“安全上下文登录”操作。我认为这很难。

设计一个域服务来处理它。

优点:无需强制登录即可解决问题 缺点:重复代码或根据上下文(请求、控制台命令或异步队列)执行相同操作的不同方法

应该由选民和域服务调用的服务

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

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

非常感谢

【问题讨论】:

【参考方案1】:

我可能更愿意在发送消息之前检查用户的权限,但让我们想想如果不合适的话我们应该如何处理。

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

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

让我们创建一个自定义标记来保存用户 ID。这是一个简单的 php 类,因此无需将其注册为服务。

<?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 的概念,所以让我们创建一个来处理来自工作人员的消息时的戳记。它将检查消息是否包含 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 传递给命令,它将对用户进行身份验证。

【讨论】:

非常感谢您提供完整而准确的回复。到目前为止,中间件是另一种选择,并且可能是主要情况下的最佳选择。我同意你的看法,最好在将消息发送给处理程序之前检查权限。在我的具体情况下,我们生成一个带有对象的事件,并且必须通过电子邮件将其通知给具有一定数量权限的具体用户。在这种情况下,我无法验证权限,因为没有任何上下文。我正在考虑创建一个 TokenInterface 并将其直接传递给 DecisionManager 投票者的概念旨在检查经过身份验证的用户的权限。不确定在发送电子邮件之前检查多个用户的权限是否完美。我将实施一项服务来过滤掉符合电子邮件通知条件的用户。或者从存储库中获取符合条件的用户。不确定哪个是最佳选择,因为我没有足够的应用程序上下文。

以上是关于在 Symfony Messenger 异步消息处理程序上使用选民/权限的最佳方式? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

Symfony Messenger:重试延迟不适用于 Redis 传输

Symfony Messenger:使用消息接口作为处理程序时重试失败的消息__invoke type-hint

通过 Symfony Messenger 执行邮件发送

Symfony 5 Messenger 不自动配置消息处理程序

Symfony Messenger / RabbitMQ 检测请求消息

Symfony Messenger 如何确定应由哪个处理程序处理每种类型的消息?