3

У меня есть ASP.NET MVC- веб-сайт с SQL Server фоновым. У меня есть несколько действий контроллера, которые требуют от меня проверки прав.Как я могу централизовать логику прав в контроллере asp.net-mvc?

Прямо сейчас, я делаю это:

public ActionResult SomeEntitledPage() 
    { 
     if (_myModel.IsMySiteAdminRole) 
     { 
      return View(new MyViewModel()); 
     } 
     else 
     { 
      return View("NotEntitled", new NotEntitledViewModel(){Page = "[PageName]", SupportDG = "[email protected]"}); 
     } 
    } 

это работает хорошо, но он чувствует, как я дублируя эту логику в ряде мест.

Каков наилучший способ (атрибут и т. Д.), Чтобы количество действий контроллера прав было «безопасным» на основе нижеследующего?

(Secure том, что он проверяет IsMySiteAdminRole и возвращает «не имеют права» вид, если не имеют права.

Я также хочу, чтобы убедиться, что я не снижение производительности на каждой странице?

+3

Основываясь на ваши downvotes на ответы у всех ниже, хотя и являются наиболее очевидными ответами, я склонен просить вас, чтобы показать вашу модель представления, так что я могу видеть, что вы там делаете. Это вызывает запах дизайна, потому что нижеприведенные решения - это довольно текстовая книга, как и идея rbac. Это не должно быть так сложно. – Sinaesthetic

+0

Прежде всего, откуда приходит _myModel? и если он отправлен из браузера, то это неправильный дизайн, любой может переопределить модель, отправленную с клиента. Если _myModel создается контроллером, то при создании самого контроллера вы можете выбросить UnauthorizedAccessException и создать для него правильную страницу ошибок. –

+2

@leora Люди, которые ответили на ваши вопросы ниже, уделяют ценное время, чтобы предоставить вам продуманные ответы на ваш вопрос. Не вежливо, чтобы люди, которые пытаются помочь вам, если они не дают вредный или глупый ответ. Учитывая, что ваш вопрос дает очень мало деталей, неудивительно, что они предоставляют ответы, которые не соответствуют вашим потребностям? – B2K

ответ

5

Я предпочитаю использовать фильтры действий для логики прав/привилегий. Красота этих фильтров заключается в том, что они могут работать ПОСЛЕ метод действия заполняет вашу модель.

Для примера:

public class AdminOnlyFilterAttribute : ActionFilterAttribute 
{ 
     public override void OnActionExecuted(ActionExecutedContext filterContext) 
     { 
     if (!filterContext.Controller.ViewData.Model.IsMySiteAdminRole) 
      { 
       filterContext.Result = new ViewResult 
       { 
        ViewName = "NotEntitled", 
        Model = new NotEntitledViewModel(){Page = "[PageName]", SupportDG = "[email protected]"} 
       }; 
      } 
     base.OnActionExecuted(filterContext); 
    } 
} 

Действие Фильтры позволяют выборочно переопределить метод OnActionExecuted вашего контроллера.

Этот атрибут может быть применен к конкретному действию или всему контроллеру. Результат будет зависеть от ваших значений модели и изменит ваш просмотр.

0

Самый лучший способ для достижения этой цели было бы использовать атрибут и декорировать действия с ним.

Посмотрите на System.Web.Mvc.AuthorizeAttribute

Вы можете унаследовать его и выполнять свою собственную пользовательскую логику. Вот SAMPL е из проекта я сделал некоторое время назад:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] 
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute 
{ 
    public bool AdminRequired; 
    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     if (UserSession.IsLoggedIn) 
      return (!AdminRequired || (AdminRequired && UserSession.IsAdmin)); 
     else 
      return false; 
    } 

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     filterContext.Result = new RedirectToRouteResult(
            new RouteValueDictionary 
            { 
             { "action", "LogIn" }, 
             { "controller", "Account" }, 
             { "returnUrl", filterContext.HttpContext.Request.RawUrl} 
            }); 
    } 
} 

С помощью этого атрибута вы можете иметь два уровня авторизации: обычный пользователь и пользователь с правами администратора. Пример:

[Authorize(AdminRequired = true)] 
public ActionResult AdminOnlyAction() 
{ 
    // perform authorized admin tasks 
} 

[Authorize] 
public ActionResult RegularUserAction() 
{ 
    // perform authorized regular user action 
} 
+0

Мне нужно только сделать это в 1 конкретном контроллере (который имеет ссылку на класс «model», который может сказать мне, разрешен ли пользователю для этой функции. Как бы я ввел это в это, поэтому я могу назвать это «_myModel. IsMySiteAdminRole "в методе AuthorizeCore? – leora

+0

Я не думаю, что атрибуты, размещенные на действиях контроллера, могут быть осведомлены о контроллере действия. Вы можете попробовать« охотиться »за ним в переданном« httpContext ». Моя рекомендация - извлечь' _myModel. IsMySiteAdminRole' в класс, который знает об «HttpContext», доступ к которому вы можете получить из атрибута. В моем примере «UserSession» является статическим классом со свойствами, которые используют «HttpContext.Current.Session» для хранения значений для текущего пользователя. – Floremin

0

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

public class BaseController : Controller 
{ 
    private readonly bool _isEntitled; 

    protected override void Initialize(RequestContext requestContext) 
    { 
     base.Initialize(requestContext); 
     // Your logic for entitlement check goes here. 
     // Set _isEntitled to true if user is entitled to view page. 
    } 

    protected override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
     if (!_isEntitled) { 
      // Redirect user which is not entitled. 
      filterContext.Result = new ViewResult 
            { 
            ViewName = "NotEntitled", 
            Model = new NotEntitledViewModel(){Page = "[PageName]", SupportDG = "[email protected]"} 
            }; 
     } 
    } 
} 

Теперь все, что вам нужно сделать, это унаследовать этот контроллер во всех контроллерах, которые должны иметь проверку права:

public MyController : BaseController 
    { 
     // Action in here... 
    } 

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

+0

I не хотят, чтобы все действия на каждом контроллере имели право ... его просто некоторые действия в некоторых контроллерах. Как бы ваш ответ удовлетворял этому требованию? – leora

0

TL; 44: Посмотрите, профиль пользователя и заполнение его информацией о роли, когда пользователь аутентифицирован.


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

Вообще говоря, ваш пользователь сеанса должен быть доступен в сеансе в виде UserProfile или расширенного идентификатора. Поступая таким образом, вы можете заполнить роли этого пользователя, а что нет, а затем проверить его на любом этапе конвейера выполнения контроллера/действия; то вы можете просто использовать пользовательский атрибут для проверки User.IsInRole («Неважно»). Вам не потребуется действие, чтобы вернуть представление «Без имени», потому что вы можете установить его в ответе в атрибуте авторизации HandlUnauthorizedRequest.

+0

Я согласен со многими ваши очки. И я увидел ваш предыдущий ответ. В этом случае, как и этот, вы вышли за пределы вопроса и перенаправили OP на «лучший» дизайн. Мне очень понравился этот ответ. В целом (и в этом случае) я сжимаю, чтобы «проповедовать» OP о том, правильный ли их подход, b/c слишком часто разработчики делают выбор и компромиссы, которые мы не можем оценить или наследовать компоненты reqs/app. –

0

Я согласен с решениями, предложенными другими!

Вы упомянули, что вам нужно дублировать код, где бы вы ни нуждались, чтобы проверить свойства IsInAdminRole. Поэтому я создал общий метод, в котором вы передаете модель и имя/путь. В методе IsEntitled просто проверьте свойство isadminrole и выполните необходимые действия.

Примечание. Это простое и примерное решение. Вы можете проверить, помогает ли это или дает некоторые указатели.

Сделайте что-то следующее.

Примеры моделей

public class NoRights 
{ 
    public string Message { get; set; } 
} 

public class MyModel 
{ 
    public bool IsAdminRole { get; set; } 
} 

Вот домашний контроллер

public class HomeController : Controller 
{ 

    public ActionResult Index() 
    { 
     var mod = new MyModel() { IsAdminRole = true }; 
     return IsEntitled(mod, "IndeX"); 
     //return View(); 
    } 
} 

А вот статический метод выборки. это может идти в классе-помощнике. Примечание. Это приведет к отображению стандартной ошибки с сообщением об ошибке, которое указано в свойстве «Сообщение» модели NoRights.

public static ViewResult IsEntitled(object model, string viewPath) 
    { 
     var prop = model.GetType().GetProperty("IsAdminRole"); 
     var hasRights = (bool)prop.GetValue(model, null); 
     var viewResult = new ViewResult(); 
     if (hasRights) 
     { 
      viewResult.ViewData = new ViewDataDictionary(model); 
      viewResult.ViewName = viewPath; 
     } 
     else 
     { 
      viewResult.ViewData = new ViewDataDictionary(
       new NoRights() { Message = "Your dont have rights" }); 
      viewResult.ViewName = "Error"; 

     } 
     return viewResult; 
    } 

Если я прохожу по истине в IsAdminRole собственности здесь является выходом.

To Home Index

Если я перехожу ложным в IsAdminRole собственности я получаю следующий вывод.

Access denied

Надеется, что это помогает.

+0

Я думаю, что вы сделали хорошую работу по инкапсуляции логики OP. Но у меня создалось впечатление, что ОП искал абстрактную работу вдали от тела метода действия. –

0

У нас был аналогичный случай, в котором требование было полностью разрешено логикой авторизации в одном централизованном месте. Для этого вы можете попробовать что-то вроде этого.

Сначала вы можете иметь регулятор уровня приложений, которые могут быть

public class ApplicationController : Controller 
{ 
    protected override void OnActionExecuted(ActionExecutedContext ctx) 
    { 
     base.OnActionExecuted(ctx); 

     if (!_myModel.IsMySiteAdminRole) 
     { 
      ctx.Result = View("NotEntitled", new NotEntitledViewModel(){Page = "[PageName]", SupportDG = "[email protected]"}); 
     } 
    } 
} 

Теперь этот контроллер может быть унаследован другими контроллерами. Вся ваша логика права теперь централизована в одном контроллере.

public class EntitlementController: ApplicationController 
{ 
    return View(new MyViewModel()); 
}