MVC4のRole Based Authenticationを利用して、ユーザーロールごとのメニューを表示する
参考URLは以下。
- http://blog.longle.net/2012/09/25/seeding-users-and-roles-with-mvc4-simplemembershipprovider-simpleroleprovider-ef5-codefirst-and-custom-user-properties/
- http://techbrij.com/custom-roleprovider-authorization-asp-net-mvc
- http://techbrij.com/role-based-menu-asp-net-mvc
おおざっぱに書くと
- Role Manager を有効にして
- 必要であればCustome Role Providerなどを利用しつつユーザーにロールを設定し
- Controller と View で Role に応じたルーティング、描画をする
という流れ。順番に書いていきます。
Role Managerを有効にする
Web.configのSystem.webセクションに roleManager セクションと membership セクションを追加します。Custom Role Providerを使う場合はここで指定しますが、今回はデフォルトの SimpleRoleProvider を利用することにしました。
<roleManager enabled="true" defaultProvider="SimpleRoleProvider"> <providers> <clear/> <add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/> </providers> </roleManager> <membership defaultProvider="SimpleMembershipProvider"> <providers> <clear/> <add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" /> </providers> </membership>
この後の検証用にDatabase MigrationのSeedで適当にRoleを作ってユーザーに付与しておきます。Roleの作成や付与はSystem.Web.Security.Rolesクラスを利用しておこないます。
protected override void Seed(SNSAuthorization.Models.UsersContext context) { WebSecurity.InitializeDatabaseConnection( "DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true); if (!Roles.RoleExists("Administrator")) { Roles.CreateRole("Administrator"); } if (!WebSecurity.UserExists("Admin")) { WebSecurity.CreateUserAndAccount("Admin", "admin"); } if (!Roles.GetRolesForUser("Admin").Contains("Administrator")) { Roles.AddUserToRole("Admin", "Administrator"); } }
Pacakge manager consoleから以下のコマンドを実行することでデータを生成することができます。
PM> update-database
Custome Role Providerなどを利用しつつユーザーにロールを設定する
今回の主題とはちがうので割愛します。こちらを参照してください。
Controller と View で Role に応じたルーティング、描画をする
コントローラー側でログインしているユーザーのRoleに応じてルーティングを処理するにはRoles#GetRolesForUserなどが利用できます。
public ActionResult RedirectToDefault()
{
String roles = Roles.GetRolesForUser();
if (roles.Contains("Administrator"))
{
return RedirectToAction("About","Home");
}
else
{
return RedirectToAction("Index");
}
}
また、リソースへのアクセス自体を制限するにはAuthorize Attributeが利用できます。
[Authorize(Role="Administrator")] public class HomeController : Controller { // ... }
Viewでも同様にRoles#GetRolesForUserを使うことで描画する内容を変更することができます。
<ul id="menu">
@if (HttpContext.Current.User.Identity.IsAuthenticated)
{
String roles = Roles.GetRolesForUser();
var links = from item in menus
where item.Roles.Split(new String { "," }, StringSplitOptions.RemoveEmptyEntries)
.Any(x => roles.Contains(x) || x == "All")
select item;
foreach (var link in links)
{
@: <li> @Html.ActionLink(link.LinkText, link.ActionName,link.ControllerName)</li>
}
}
else{
var links = from item in menus
where item.Roles.Split(new String{","},StringSplitOptions.RemoveEmptyEntries)
.Any(x=>new String[]{"All","Anonymous"}.Contains(x))
select item;
foreach ( var link in links){
@: <li> @Html.ActionLink(link.LinkText, link.ActionName, link.ControllerName)</li>
}
}
</ul>
まとめ
Role Managerを利用することで割りと簡単にRoleごとの振る舞いを記述することができます。
素敵ですね :)
[追記]
上記のようにロールごとに表示するメニューを変更しようとすると、HomeControllerから操作するViewでWebSecurityクラスのメソッドを利用することになります。
このとき、何も設定を変更していないと以下のようなExceptionが投げられます。
"WebSecurity.InitializeDatabaseConnection" メソッドを呼び出してから、"WebSecurity" クラスのその他のメソッドを呼び出す必要があります。この呼び出しは、サイトのルートにある _AppStart.cshtml ファイル内に配置される必要があります。
どうもアプリを起動するごとにWebSecurity#InitializeDatabaseConnectionを使って、Membership が一度だけ初期化されるようにする必要があるようです。
今回の例のようにSimpleMembershipProviderを利用している場合は、単純にInitializeSimpleMembershipアトリビュートを付ければOKです。
[InitializeSimpleMembership] public class HomeController : Controller { // ... }
コード本体はFilters/InitializeSimpleMembershipAttribute.csになります。向き先のDBを変更するときなどはここをいじることになりそうです。