ASP.NET MVC: авторизация внутри Action - предлагаемые шаблоны или это запах?

У меня есть приложение ASP.NET MVC, использующее атрибуты авторизации на контроллерах и действиях. Это хорошо работает, но появилась новая морщина.

Объект: Отгрузка

Роли: доставка, учет, общий пользователь

Отгрузка перемещается через рабочий процесс. В состоянии А его можно отредактировать только по отгрузке. В состоянии B его можно редактировать только с помощью учета.

У меня есть ShipmentController и действие Edit. Я могу поместить атрибут авторизации, чтобы ограничить действие "Правка" только этими двумя ролями, но это не отличает, из какого состояния находится отправка. Мне нужно будет сделать некоторую авторизацию внутри действия перед вызовом службы, чтобы определить, является ли пользователь действительно разрешено выполнять действие редактирования.

Итак, у меня остались два вопроса:

1) Каким образом можно получить авторизацию внутри Action. Действие контроллера вызывает службу, а затем служба выполняет соответствующие вызовы для объекта "Отгрузка" (количество обновлений, дата обновления и т.д.). Я точно знаю, что я хочу, чтобы объект отправки был агностик любых требований авторизации. С другой стороны, у меня нет реального понимания, если я хочу, чтобы объект службы знал об авторизации или нет. Есть ли хорошие шаблоны для этого?

2) Является ли моя проблема на самом деле симптомом плохого дизайна? Вместо ShipmentController я должен иметь StateAShipmentController и StateBShipmentController? У меня нет никакого полиморфизма, встроенного в объект Shipment (состояние - это просто перечисление), но, возможно, мне следует и, возможно, контроллеры должны это отразить.

Я предполагаю, что я использую более общие решения, а не конкретные для моего дела. Я просто хотел привести пример, чтобы проиллюстрировать вопрос.

Спасибо!

Ответ 1

Ваш атрибут авторизации может получить Отправление из параметров действия или данных маршрута, а затем принять решение.

Для номера 1 существует несколько шаблонов, которые позволяют использовать богатое поведение в объектах домена. При двойной отправке вы передаете ссылку на абстракцию (интерфейс) службы на метод объекта. Тогда он может сделать свое дело. Вы также можете написать приложение, которое отправляет Отгрузку и выполняет работу.

По номеру 2 необязательно. Возможно, вам нужно будет абстрагировать концепцию "контекстной доставки" в службу, которая определяет, в каком контексте пересылки вы находитесь. Но я бы сказал, что до тех пор, пока вам это не понадобится.

Ответ 2

Я не вижу проблемы с тем, что метод Action выполняет дальнейшие проверки полномочий внутри него. Вы можете использовать поставщика ролей для получения мелкого разрешения, которое вы ищете. Пожалуйста, извините мой синтаксис здесь - это, вероятно, грубо, и я не тестировал это.

[Authorize(Roles="Shipping, Accounting")]
public ActionResult Edit(int id)
{
    Shipment shipment = repos.GetShipment(id);


    switch (shipment.State)
    {
         case ShipmentState.A:
         if (Roles.IsUserInRole("Shipping"))
                return View(shipment);
         else
                return View("NotAuthorized");
         break;
         case ShipmentState.B:
         if (Roles.IsUserInRole("Accounting"))
                return View(shipment);
         else
               return View("NotAuthorized");
         break;
         default:
              return View("NotAuthorized");
     }
}

Ответ 3

Вы можете взглянуть на Rhino.Security, его можно использовать для реализации авторизации пользователя в этих сценариях.

Ответ 4

В дополнение к ответу выше вы можете вернуть HttpUnauthorizedResult вместо создания собственного представления NotAuthorized. Это перенаправит страницу входа и будет вести себя так же, как обычный атрибут [Авторизовать]