Авторизация команд в Axon

До сих пор я обрабатывал авторизацию в CommandHandlers.

Пример: у меня есть совокупная команда, содержащая список менеджеров (AggregateIdentifier от пользователя). Все обработчики команд в агрегате группы затем проверяют, что пользователь, выполняющий команду, является менеджером группы. UserId вводится как метаданные в CommandHandlerInterceptor на основе SecurityContext.

Моя основная проблема заключается в том, что когда я использую саги, это становится дополнительными накладными расходами для поддержания пользовательского контекста в командах, выдаваемых для разных агрегатов. Кроме того, связь с менеджером может истечь в период выполнения саги и последующих неудачных команд, что приведет к неполному состоянию, которое также необходимо обработать с помощью некоторых функций отката.

Лучше выполнить авторизацию на уровне моего контроллера, чтобы избежать дополнительных накладных расходов, или я должен рассматривать это как более эффективную практику, позволяя моим CommandHandlers решать, действительна ли команда для агрегата?


person Vincent    schedule 02.08.2020    source источник


Ответы (1)


Авторизация для выполнения определенных операций / команд - это то, что, я бы сказал, не является логикой, зависящей от предметной области. Напротив, это скорее форма сквозной заботы, которая нужна вам на протяжении всего вашего приложения. Таким образом, размещение его в @CommandHandler аннотированном методе - не лучшее место в моей голове. Тем не менее, размещение его рядом имеет большой смысл.

Вы указали, что уже используете CommandHandlerInterceptor для заполнения Spring SecurityContext, поэтому я предполагаю, что вы используете CommandDispatchInterceptor для заполнения MetaData команды информацией при отправке команды. Это действительно отличное использование логики перехватчика, поэтому я бы оставил это на месте. Однако это устанавливает информацию, но не проверяет ее.

С этой целью вы можете создать свой собственный Handler Enhancer, который проверяет метаданные безопасности команды. Вы даже можете создать специальную аннотацию, которую вы добавите рядом с аннотацией @CommandHandler, которая описывает необходимые роли. Таким образом, метод по-прежнему отображает, какие роли вам нужны для данной команды, но фактическая проверка может быть выполнена за вас в этом Handler Enhancer.

А теперь вернемся к вашему вопросу:

Лучше выполнить авторизацию на уровне моего контроллера, чтобы избежать дополнительных накладных расходов, или я должен рассматривать это как более эффективную практику, позволяя моим CommandHandlers решать, действительна ли команда для агрегата?

Я думаю, что это нормально делать это в совокупности, потенциально делая его чище за счет использования Handler Enhancer. Что касается вашего беспокойства в саге, я думаю, вам следует рассмотреть это отдельно. Сага обрабатывает события, факты того, что что-то произошло. Игнорирование этого факта, потому что кто-то, кто инициировал операции, которые привели к этому факту, не имеет прав, не решает того факта, что это все еще произошло. Добавим, что у вас действительно нет никаких гарантий относительно времени выхода саги. Может быть, ваша сага связана с историческими событиями, а это значит, что она полностью выходит за рамки.

Если это возможно в вашей системе, я бы рассмотрел любую команду, которую Saga хочет опубликовать, как отправленную системным пользователем. Сага - это не то, на что ваши пользователи (у которых есть определенные роли) будут напрямую влиять; это все косвенно. Сага является внутренней по отношению к вашей системе, следовательно, это система, описывающая намерение выполнить операцию.

Это мои два цента к ситуации, надеюсь, это поможет вам @Vincent!

person Steven    schedule 03.08.2020
comment
Спасибо, это действительно помогло получить второе мнение. Я обновил свой CommandHandlerInterceptor, чтобы проверить, не является ли RequestContextHolder.getRequestAttributes() нулевым. Если это так, он извлечет идентификатор пользователя и роли из контекста безопасности и установит для внутреннего поля метаданных значение FALSE. Если он равен нулю, то я предполагаю, что это вызов внутренней службы, и просто устанавливаю для внутреннего поля метаданных значение ИСТИНА. Пример Я добавил HandlerEnhancer, где в WrappedMessageHandlingMember метод canHandle затем проверяет этот внутренний флаг. - person Vincent; 04.08.2020
comment
Приятно слышать @Vincent! Рад, что помогли :-) - person Steven; 05.08.2020
comment
Дополнительный вопрос. Я запустил его, выполнив простую проверку того, что у пользователя есть правильные роли из контекста безопасности, но у меня есть другой сценарий, в котором я хочу извлечь свою совокупность Team с помощью репозитория из HandlerDefinition. Чтобы решить эту проблему, я подумал о том, чтобы автоматически подключить репозиторий, но это просто кажется неуклюжим, и я не могу найти хороший способ, кроме использования его в качестве параметра для моего конструктора моего расширения WrappedMessageHandlingMember, чтобы использовать его в методе canHandle - person Vincent; 08.08.2020
comment
Является ли это Team агрегирование фактическим агрегированием, которое собирается выполнить команда? Или это другой агрегат, через который вам нужно проверить состояние? - person Steven; 10.08.2020
comment
Я вижу, что я немного плохо описал свой последующий вопрос, извините за это :) У меня есть Team Aggregate, в котором будет выполняться команда. Еще у меня есть Event агрегат. Оба агрегата Team и Event содержат List<ProfileId> managers; Если пользователь присутствует в любом из списков менеджеров, ему разрешено выполнять команду. Они связаны друг с другом на основе TeamId агрегатного идентификатора, который представляет собой сочетание UUID и EventId (агрегированный идентификатор для Event). Цель состоит в том, чтобы получить агрегат Team и агрегат Event и проверить список менеджеров для обоих. - person Vincent; 16.08.2020
comment
Дополнение: я попытался аннотировать реализованный HandlerEnhancerDefinition с помощью @Component и использовать как инъекцию на основе конструктора, так и инъекцию на основе установщика, но в обоих случаях они вводятся, но метод wrapHandler никогда не вызывается, когда я это делаю. Если вместо этого я ничего не @Autowire, wrapHandler вызывается правильно. Моя идея состоит в том, чтобы добавить внедренные bean-компоненты в качестве параметров в конструктор моего WrappedMessageHandlingMember. - person Vincent; 16.08.2020
comment
Ааа, значит, вам нужно содержимое Aggregate для формирования решения по безопасности, верно? Возможно, будет полезно взглянуть на аннотацию @CommandHandlerInterceptor, которую вы можете использовать для аннотирования методов в самом Aggregate для перехвата сообщения. По сути, он похож на MessageHandlerInterceptor, но с дополнительным преимуществом знания состояния агрегата: docs.axoniq.io/reference-guide/axon-framework/ - person Steven; 17.08.2020