Безопасность в 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)