web-dev-qa-db-ja.com

ASP.NetMVCセキュリティに基づくメニュー項目の非表示/表示

ASP.Net MVC3サイトで作業しています。 _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コードを使用して、最初はそのままにしておく必要がありますか?

15
Gavin

これが私がやったことです。メニュー項目ごとに静的ブールプロパティを使用して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>}
8
Gavin

役割に関する一般的なアドバイス

私がこれを行った方法は、カスタムプリンシパルを作成し、そこに追加の必要な情報を格納することです。あなたの例では、これには少なくともユーザーの役割が含まれます。 こうすることで、ユーザーストア(SQLデータベースである可能性が高い)に何度もアクセスする必要がなくなります。

私が正常に使用しているコードを提供する私のこの質問を見てください: Base Controller 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;
    }
  }
}

私は実際にこれをさらに一歩進めて、ReportsLinkViewModelを作成し、ユーザーが許可されている場合はリンクを含むように設定し、許可されていない場合は空の文字列になるように設定します。次に、ビューには次のように表示されます。

@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));
10
Tom Chantler

部分ビューを作成し、コントローラーからビューを返します。

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")
0
live-love