SimpleMembershipをCode Firstに組み込む
ザクです。
MVC4アプリで認証(authentication)、認可(authorization)をしたい時はSimpleMembershipが便利です。何しろMicrosoft製ですし、MVC4アプリのプロジェクトをVS2012で作成したら自動で組み込まれています。ただ、ユーザー関連のモデル(4つあります。DBのテーブルとしては5つです)は、そのままではCode Firstで管理されていません。こまごま調整して組み込んでやる必要があります。
DAL、モデル、コントローラでのコンテキストを調整する
まず、DALにSimpleMembershipのモデル群を登録しましょう。
public SchoolContext() : base("DefaultConnection") { } public DbSet<UserProfile> UserProfiles { get; set; } public DbSet<Membership> Membership { get; set; } public DbSet<Role> Roles { get; set; } public DbSet<OAuthMembership> OAuthMembership { get; set; }
//OnModelCreatingの中に以下のコードを追加 modelBuilder.Entity<Membership>() .HasMany<Role>(r => r.Roles) .WithMany(u => u.Members) .Map(m => { m.ToTable("webpages_UsersInRoles"); m.MapLeftKey("UserId"); m.MapRightKey("RoleId"); });
DALのファイルはSchoolContex.csでしたね。DefaultConnectionの真下に上の4行を追加しましょう。UsersInRolesテーブルも必要ですが、これはリンクテーブルなのでOnModelCreatingの中に定義します。
次にモデルです。SimpleMembershipで使うモデルは、MVC4アプリを作ったらModelsフォルダの中にAccountModels.csとして作られているはずです。UserProfilesだけがありますね。他の3つのモデルを定義してやります。
//UserProfilesの下 [Table("webpages_Membership")] public class Membership { public Membership() { Roles = new List<Role>(); OAuthMemberships = new List<OAuthMembership>(); } [Key, DatabaseGenerated(DatabaseGeneratedOption.None)] public int UserId { get; set; } public DateTime? CreateDate { get; set; } [StringLength(128)] public string ConfirmationToken { get; set; } public bool? IsConfirmed { get; set; } public DateTime? LastPasswordFailureDate { get; set; } public int PasswordFailuresSinceLastSuccess { get; set; } [Required, StringLength(128)] public string Password { get; set; } public DateTime? PasswordChangedDate { get; set; } [Required, StringLength(128)] public string PasswordSalt { get; set; } [StringLength(128)] public string PasswordVerificationToken { get; set; } public DateTime? PasswordVerificationTokenExpirationDate { get; set; } public ICollection<Role> Roles { get; set; } [ForeignKey("UserId")] public ICollection<OAuthMembership> OAuthMemberships { get; set; } } [Table("webpages_OAuthMembership")] public class OAuthMembership { [Key, Column(Order = 0), StringLength(30)] public string Provider { get; set; } [Key, Column(Order = 1), StringLength(100)] public string ProviderUserId { get; set; } public int UserId { get; set; } [Column("UserId"), InverseProperty("OAuthMemberships")] public Membership User { get; set; } } [Table("webpages_Roles")] public class Role { public Role() { Members = new List<Membership>(); } [Key] public int RoleId { get; set; } [StringLength(256)] public string RoleName { get; set; } public ICollection<Membership> Members { get; set; } } //RegisterExternalLoginModelの上
ここらで一度ビルドしてみます。Filterフォルダ内にあるInitializeSimpleMembershipAttribute.csファイルやAccountController.csで、「'UsersContext'が見つかりませんでした。」というエラーが出ます。UsersContextは、MVC4プロジェクトを作成したときに、勝手につけられるSimpleMembership用のDBコンテキスト名です。Code Firstを使う時は、DBコンテキストはDALで定義しているもの一つにしたいので、これを使わないように既存の設定を調整します。なぜ、DBコンテキストを一つにしたいかというと、Code Firstで管理できるDBコンテキストが1つまでと決まっているからです。(MVC4に含まれるEF5の場合に限る。MVC5のEF6は複数定義できるそうです)
FilterフォルダのInitializeSimpleMembershipAttribute.csファイルをプロジェクトから除外します。ファイルを右クリックで、同名のメニュー選択ですね。次に、AccountController.csの先頭のほうにある[InitializeSimpleMembership]を消します。これは、コントローラで利用できるフィルター属性で、先ほど除外したファイルを呼び出し、ユーザー関連のモデルのテーブルがDBに存在しなければ、自動で作成するものです。Code Firstでユーザーのテーブルを作るので、自動生成はしません。
ビルドします。まだ、AccountController.csでエラーが出ますね。ExternalLoginConfirmationアクションの中です。ここでもUsersContextを使った行があります。そのまま使うには、このコンテキストではなく、SchoolContextの方を使うように変えます。「using (UsersContext~」となっている辺りをコメントアウトしてください。下のクローズする部分も同様にコメントアウトです。次に、コントローラの先頭でSchoolContextの呼び出しを追加してやります。
namespace MvcApplication1.Controllers { [Authorize] public class AccountController : Controller { private SchoolContext db = new SchoolContext(); // // GET: /Account/Login
SchoolContextがどこにあるのかわからないので、usingを追加してやりましょう。
using MvcApplication1.DAL;
ビルドします。今度はエラーを出さずにビルドできました。
ユーザーモデルをマイグレーション
ここまで準備できたら、Code Firstのマイグレーションでユーザーモデルを取り込みます。add-migrationします。
add-migration AddSimpleMembership
DBにテーブルを作成するマイグレーションファイルが作成されました。テーブルだけできても仕方がないので、updateする前にユーザーのシードデータも用意してやりましょう。Configuration.csに書くんでしたよね。
//以下はコメントアウトするか削除する。 //public Configuration() //{ // AutomaticMigrationsEnabled = false; //} protected override void Seed(SchoolContext context) { // Seed for UserProfile WebSecurity.InitializeDatabaseConnection( "DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true); if (!WebSecurity.UserExists("testuser")) WebSecurity.CreateUserAndAccount( "testuser", "testuser", new{} );
Seedメソッドの先頭に上のようにコードを追加します。上の部分のConfigurationのコンストラクタは使わないので、コメントアウトするか削除してください。SimpleMembershipを使うときに使えるメソッドは、WebSecurityに入っているので、これをusingに加えてください。
ロールマネージャーを有効にする
もういいかと思って、update-databseしてしまった人は悔い改めてください。「ロール マネージャー機能は有効にされていません。」というエラーが出たと思います。そうです、SimpleMembershipをちょー便利に使うには、RoleMangerを有効にせねばなりません。これを有効にすることでコントローラに「Autorization(認可)」の属性フィルターが使えるようになります。これについてはまた後日解説します。
RoleMangerを有効にするには、Web.configに設定を追加します。(いろいろ面倒くさいですね)
<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>
Web.configの
update-database
SimpleMembershipのテーブルが作成されました。シードデータのtestuserもちゃんと入っています!