【问题标题】:ASP.Net MVC Hide/Show Menu Items Based On SecurityASP.Net MVC 基于安全性隐藏/显示菜单项
【发布时间】:2012-01-14 17:20:50
【问题描述】:

我正在开发一个 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;

然而事实证明,在生命周期的这一点上,用户对象为空。我也尝试过创建自定义全局过滤器,但 ViewData 无法访问。

做这种事情的推荐方法是什么?我是否应该将视图中的所有 HttpContext 代码保持原样?

【问题讨论】:

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


【解决方案1】:

关于角色的一般建议

我这样做的方法是创建一个自定义主体并在其中存储额外的必需信息。在您的示例中,这至少包括用户的角色。 这样你就可以避免对用户存储(很可能是一个 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;
    }
  }
}

我实际上倾向于更进一步,在我的ViewModel 中创建一个ReportsLink,如果用户被授权,则将其设置为包含链接,如果用户未授权,则将其设置为空字符串。然后视图只是说:

@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));

【讨论】:

    【解决方案2】:

    这就是我最终要做的。我为每个菜单项创建了一个名为 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>}
    

    【讨论】:

    • 我想你只有 3 组。但是如果你有 20 - 30 个组会发生什么?
    【解决方案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")
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-19
      • 1970-01-01
      • 2017-04-17
      • 2014-09-22
      • 1970-01-01
      • 2019-04-08
      • 2017-07-16
      相关资源
      最近更新 更多