ASP.Net MVC隐藏/显示基于安全性的菜单项

时间:2011-12-07 06:23:08

标签: asp.net-mvc-3 asp.net-membership

我正在开发一个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对象为null。我也尝试过创建自定义全局过滤器,但ViewData无法访问。

这样做的推荐方法是什么?我是否应该首先使用视图中的所有HttpContext代码保留它的原理?

3 个答案:

答案 0 :(得分:10)

关于角色的一般建议

我这样做的方法是创建一个自定义主体并在那里存储额外的必需信息。在您的示例中,这至少包括用户的角色。 这样你就可以避免多次访问用户存储(可能是SQL数据库)。

看看我的这个问题,我在其中提供了我成功使用的代码:Is this Custom Principal in Base Controller ASP.NET MVC 3 terribly inefficient?

请注意,我将自定义主体存储在缓存中而不是存储在会话中(仅仅是关于会话劫持的偏执)。

我喜欢这种方法,因为它非常易于扩展。例如,我已经扩展了这个,以便在用户通过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));

答案 1 :(得分:8)

这是我最终做的事情。我为每个菜单项创建了一个名为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 :(得分:0)

创建局部视图并从控制器返回视图:

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")