ASP.Net MVC Скрыть/Показать элементы меню на основе безопасности

Я работаю над сайтом ASP.Net MVC 3. В главном представлении _Layout есть меню, и я хочу скрыть некоторые элементы в меню на основе, если вы вошли в систему и какие роли вы находитесь.

В настоящее время это работает с использованием этого кода

@if (HttpContext.Current.User.Identity.IsAuthenticated)
{
   <li id="MyLearningTab">@Html.ActionLink("My Learning", "MyLearning", "Learning")</li> 
   if (HttpContext.Current.User.IsInRole("Reporters"))
   {
      <li id="ReportTab">@Html.ActionLink("Reports", "Index", "Reports")</li>
   }
   if (HttpContext.Current.User.IsInRole("Administrators"))
   {
      <li id="DashboardTab">@Html.ActionLink("Dashboard", "Dashboard", "Admin")</li>
      <li id="AdminTab">@Html.ActionLink("Admin", "Index", "Admin")</li> 
   }
}

Я хотел бы реорганизовать это в нечто более читаемое и придумал что-то вроде этого

@if ((bool)ViewData["MenuMyLearning"]){<li id="MyLearningTab">@Html.ActionLink("My Learning", "MyLearning", "Learning")</li> }    
@if((bool)ViewData["MenuReports"]){<li id="ReportTab">@Html.ActionLink("Reports", "Index", "Reports")</li>}
@if ((bool)ViewData["MenuDashboard"]){<li id="DashboardTab">@Html.ActionLink("Dashboard", "Dashboard", "Admin")</li>}
@if ((bool)ViewData["MenuAdmin"]){<li id="AdminTab">@Html.ActionLink("Admin", "Index", "Admin")</li>}

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

ViewData["MenuDashboard"] = User != null && User.Identity.IsAuthenticated && User.IsInRole("Administrators");
ViewData["MenuAdmin"] = User != null && User.Identity.IsAuthenticated && User.IsInRole("Administrators");
ViewData["MenuReports"] = User != null && User.Identity.IsAuthenticated && User.IsInRole("Reportors");
ViewData["MenuMyLearning"] = User != null && User.Identity.IsAuthenticated;

Однако оказывается, что объект User в данный момент жизненного цикла является нулевым. Я также попытался создать настраиваемый глобальный фильтр, но ViewData тогда недоступен.

Каков рекомендуемый способ сделать что-то подобное? Должен ли я просто оставить его, как это было сначала со всем кодом HttpContext в представлении?

Ответы

Ответ 1

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

public static bool DashboardVisible
{
   get 
   { 
      return 
         HttpContext.Current.User != null && 
         HttpContext.Current.User.Identity.IsAuthenticated; 
   }
}

Затем я прибрал частичное представление моего меню, чтобы выглядеть так:

@if (MenuSecurity.ReportsVisible){<li id="ReportTab">@Html.ActionLink("Reports", "Index", "Reports")</li>}
@if (MenuSecurity.DashboardVisible){<li id="DashboardTab">@Html.ActionLink("Dashboard", "Dashboard", "Admin")</li>}
@if (MenuSecurity.AdminVisible){<li id="AdminTab">@Html.ActionLink("Admin", "Index", "Admin")</li>}

Ответ 2

Общие советы о ролях

То, как я это сделал, - создать пользовательский принцип и сохранить там дополнительную требуемую информацию. В вашем примере это, по крайней мере, включает роли для пользователя. Таким образом, вы избегаете делать много дополнительных поездок в хранилище пользователей (что, скорее всего, является базой данных SQL).

Посмотрите на мой вопрос, в котором я даю код, который успешно использую: Является ли этот пользовательский принцип в базовом контроллере ASP.NET MVC 3 ужасно неэффективным?

Обратите внимание, что я сохраняю пользовательский принцип в кеше, а не в сеансе (просто параноик о захвате сеанса).

Мне нравится этот подход, поскольку он очень расширяем. Например, с тех пор я расширил это, чтобы выставить учетные данные Facebook, когда пользователь входит в систему через Facebook.

Просто помните, что если вы кешируете данные, вам нужно запомнить их, когда они меняются!

Отвечайте на свой вопрос

Просто добавьте в свой конкретный случай, вероятно, вы должны сохранить эту дополнительную информацию в ViewModel, а затем в вашем представлении будут произноситься такие вещи, как:

@if(ShowReports) { <li id="ReportTab">@Html.ActionLink("Reports", "Index", "Reports")</li> }
@if(ShowDashboard) { <li id="DashboardTab">@Html.ActionLink("Dashboard", "Dashboard", "Admin")</li> }
@if(ShowAdmin { <li id="AdminTab">@Html.ActionLink("Admin", "Index", "Admin")</li> }

с кодом ViewModel, который говорит что-то вроде:

public bool ShowReports {get;set;}
public bool ShowDashboard {get;set;}
public bool ShowAdmin {get;set;}

public void SetViewModel()
{
  if (User.Identity.IsAuthenticated)
  {
    if (HttpContext.Current.User.IsInRole("Reporters"))
    {
       ShowReports = true;
    }
    if (HttpContext.Current.User.IsInRole("Administrators"))
    {
       ShowDashboard = true;
       ShowAdmin = true;
    }
  }
}

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

@Model.ReportsLink
@Model.DashboardLink
@Model.AdminLink

В этом случае подходящая часть ViewModel может быть такой:

ReportLink = new MvcHtmlString(HtmlHelper.GenerateLink(HttpContext.Current.Request.RequestContext, System.Web.Routing.RouteTable.Routes, "linktext", "routename", "actionname", "controllername", null, null));

Ответ 3

Создайте частичное представление и верните его из контроллера:

LayoutViewModel.cs:

public class LayoutViewModel
{   ...
    public bool ShowAdmin { get; set; }
}

LayoutController.cs:

public PartialViewResult GetAdminMenu()
    {
        LayoutViewModel model = new LayoutViewModel();            

        model.ShowAdmin = userHasPermission("Admin"); // change the code here

        return PartialView("_AdminMenu", model);
    }

_AdminMenu.cshtml (частичное представление):

@model DelegatePortal.ViewModels.LayoutViewModel


@if (@Model.ShowAdmin)
{
   <!-- admin links here-->
}

_Layout.csthml (основной вид):

...
              @Html.Action("GetAdminMenu", "Layout")