Безопасность в MVC-представлениях

В моем приложении MVC у меня есть несколько разных ролей: Admin, General User и т.д. и т.д.

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

[Authorize(Roles="Admin")]
public ActionResult Create()
{
    return View();
}

Но мне также необходимо применить некоторую безопасность к представлениям, чтобы не отображать определенные разделы представления для определенных ролей:

@if( User.IsInRole("Admin") )
{
    @Html.ActionLink("Create", "Create")
}

Лучше ли это сделать выше, или обрабатывать такую ​​защиту в ViewModel:

public ActionResult Index()
{
    var model = new IndexViewModel();

    model.CanCreate = User.IsInRole("Admin");

    return View(model);
}

View:
@( Model.CanCreate )
{
    @Html.ActionLink("Create", "Create")
}

Есть ли у второго метода какие-либо преимущества по сравнению с первым или это просто предпочтение?

Ответ 1

Второй вариант более предпочтителен, так как ваша бизнес-логика останется на уровне модели.

В вашем примере бизнес-логика очень проста. Однако представьте, что требования изменились, и теперь не только Админы могут создавать контент, но и Общие пользователи, которые подписались более 1 месяца назад. С учетом бизнес-логики вам придется обновлять все ваши взгляды.

Ответ 2

Один из способов, которым я это сделал раньше, - создать фильтр действий, который наследуется от AuthorizeAttribute. Фильтр можно назвать чем-то вроде DisplayIfAuthorizedAttribute и в дополнение к стандартным свойствам AuthorizeAttribute имеет свойство ViewNameIfNotAuthorized.

Атрибут вызывает базовый метод для авторизации, а если он терпит неудачу, возвращается представление ViewNameIfNotAuthorized. В противном случае он позволяет обычно действовать.

Затем вы будете отображать эти частичные представления с помощью методов действий и вызывать методы действий через Html.RenderAction или Html.Action в родительском представлении. Эти действия будут украшены атрибутом.

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

Вот как выглядит фильтр:

public class DisplayIfAuthorizedAttribute : System.Web.Mvc.AuthorizeAttribute
{
    private string _ViewNameIfNotAuthorized;
    public DisplayIfAuthorizedAttribute(string viewNameIfNotAuthorized = null)
    {
        _ViewNameIfNotAuthorized = viewNameIfNotAuthorized;
    }
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        bool isAuthorized = base.AuthorizeCore(filterContext.HttpContext);

        if (!isAuthorized)
        {
            filterContext.Result = GetFailedResult();
        }
    }

    private ActionResult GetFailedResult()
    {
        if (!String.IsNullOrEmpty(_ViewNameIfNotAuthorized))
        {
            return new ViewResult { ViewName = _ViewNameIfNotAuthorized };
        }
        else
            return new EmptyResult();
    }
}

Ваш метод действия будет украшен как:

[DisplayIfAuthorized("EmptyView", Roles="Admin")]
        public ViewResult CreateLink()
        {
            return View("CreateLink");
        }

Ответ 3

Вам могут понадобиться оба...

Обратите внимание, что второй только один не был бы безопасным, пользователь мог бы создать URL-адрес для actionlink в адресной строке браузера. Поэтому вам абсолютно необходим атрибут безопасности.

Второй вариант - это вопрос удобства пользователя или дизайна пользовательского интерфейса. Возможно, вы хотите, чтобы пользователь мог нажать "Создать", а затем выбрать другой вариант входа.

Ответ 4

Проверьте авторизацию в контроллере и подготовьте Viewmodel для представления в соответствии с вашими правилами роли.

Представления используются для простого отображения данных. Итак, imo, им не нужно выполнять проверку роли и т.д.

Итак, подготовьте ViewModel с данными, которые он должен иметь, и пусть View только отобразит их. (логическое свойство вы используете его достаточно imo)