次の方法で共有


ASP.NET メンバーシップ認証から ASP.NET Core 2.0 Identity に移行する

著者: Isaac Levin

この記事では、メンバーシップ認証を使用する ASP.NET アプリ用のデータベース スキーマを ASP.NET Core 2.0 Identity に移行する方法について説明します。

Note

このドキュメントでは、ASP.NET メンバーシップ ベースのアプリ用のデータベース スキーマを、ASP.NET Core Identity に使用されるデータベース スキーマに移行するために必要な手順を示します。 ASP.NET メンバーシップ ベースの認証から ASP.NET Identity への移行について詳しくは、SQL メンバーシップから ASP.NET Identityへの既存アプリの移行に関する記事をご覧ください。 ASP.NET Core Identity について詳しくは、「ASP.NET Core での Identity の概要」をご覧ください。

メンバーシップ スキーマのレビュー

ASP.NET 2.0 より前の開発者は、アプリの認証と認可のプロセス全体を自分で作成する必要がありました。 ASP.NET 2.0 でメンバーシップが導入され、ASP.NET アプリ内でのセキュリティの処理に定型ソリューションが提供されるようになりました。 開発者は、ASP.NET SQL Server 登録ツール (Aspnet_regsql.exe) を使用して、スキーマを SQL Server データベースにブートストラップできるようになりました (サポートされなくなっています)。 このコマンドを実行すると、次のテーブルがデータベースに作成されます。

Membership Tables

既存のアプリを ASP.NET Core 2.0 Identity に移行するには、これらのテーブルのデータを、新しい Identity スキーマによって使用されるテーブルに移行する必要があります。

ASP.NET Core Identity 2.0 スキーマ

ASP.NET Core 2.0 は、ASP.NET 4.5 で導入された Identity の原則に従います。 原則は共有されていますが、実装はフレームワーク間で異なるだけでなく、ASP.NET Core のバージョン間でさえ異なります (認証と Identity の ASP.NET Core 2.0 への移行に関する記事をご覧ください)。

ASP.NET Core 2.0 Identity 用のスキーマを見る最も簡単な方法は、ASP.NET Core 2.0 アプリを新しく作成することです。 Visual Studio 2017 で次の手順のようにします。

  1. ファイル>新規作成>プロジェクトを選択します。

  2. CoreIdentitySample という名前で、新しい ASP.NET Core Web アプリケーション プロジェクトを作成します。

  3. ドロップダウン リストで [ASP.NET Core 2.0] を選んでから、[Web アプリケーション] を選びます。 このテンプレートからは、Razor Pages アプリが生成されます。 [OK] をクリックする前に、[認証の変更] をクリックします。

  4. Identity テンプレートに対して [個人のユーザー アカウント] を選びます。 最後に、[OK] をクリックしてから、[OK] をクリックします。 Visual Studio により、ASP.NET Core Identity テンプレートを使ってプロジェクトが作成されます。

  5. [ツール]>[NuGet パッケージ マネージャー]>[パッケージ マネージャー コンソール] を選んで、[パッケージ マネージャー コンソール] (PMC) ウィンドウを開きます。

  6. PMC でプロジェクト ルートに移動し、Entity Framework (EF) CoreUpdate-Database コマンドを実行します。

    ASP.NET Core 2.0 Identity は EF Core を使用して、認証データが格納されているデータベースと対話します。 新しく作成したアプリを機能させるには、このデータを格納するためのデータベースが必要です。 新しいアプリを作成した後、データベース環境のスキーマを調べる最も簡単な方法は、EF Core移行を使ってデータベースを作成することです。 このプロセスにより、ローカル環境または他の場所に、そのスキーマに基づくデータベースが作成されます。 詳しくは、前に示したドキュメントをご覧ください。

    EF Core のコマンドでは、appsettings.json で指定されているデータベースの接続文字列が使用されます。 次の接続文字列の対象は、localhost にある asp-net-core-identity という名前のデータベースです。 この設定では、DefaultConnection の接続文字列を使用するように EF Core が構成されます。

    {
      "ConnectionStrings": {
        "DefaultConnection": "Server=localhost;Database=aspnet-core-identity;Trusted_Connection=True;MultipleActiveResultSets=true"
      }
    }
    
  7. [表示]>[SQL Server オブジェクト エクスプローラー] を選びます。 appsettings.jsonConnectionStrings:DefaultConnection プロパティで指定されているデータベース名に対応するノードを展開します。

    Update-Database コマンドにより、スキーマで指定されているデータベースと、アプリの初期化に必要なデータが作成されました。 次の図は、前の手順で作成されたテーブルの構造を示したものです。

    Identity Tables

スキーマを移行する

メンバーシップと ASP.NET Core Identity のテーブルの構造とフィールドには、微妙な違いがあります。 ASP.NET と ASP.NET Core アプリでは認証と認可のパターンが大幅に変更されています。 Identity で引き続き使用されている主なオブジェクトは、UsersRoles です。 次に示すのは、UsersRolesUserRoles の対応のテーブルです。

Users

Identity
(dbo.AspNetUsers) 列
Type Membership
(dbo.aspnet_Users / dbo.aspnet_Membership) 列
Type
Id string aspnet_Users.UserId string
UserName string aspnet_Users.UserName string
Email string aspnet_Membership.Email string
NormalizedUserName string aspnet_Users.LoweredUserName string
NormalizedEmail string aspnet_Membership.LoweredEmail string
PhoneNumber string aspnet_Users.MobileAlias string
LockoutEnabled bit aspnet_Membership.IsLockedOut bit

IsLockedOutLockoutEnabled にマップされません。 IsLockedOut は、ユーザーが何度もログインに失敗して、一定時間ロックアウトされた場合に設定されます。 LockoutEnabled では、サインイン試行を何度も失敗したユーザーをロックアウトできます。 ユーザーがサインイン試行に何度も失敗した場合、LockoutEnd は将来の日付に設定され、ユーザーはその日付までサインインできません。 LockoutEnabled が false の場合、サインイン試行を何度も失敗したユーザーがロックアウトされることはありません。 OWASP ごとに、いくつかの試行が失敗した後の一時的なアカウントのロックアウトは、正当なユーザーに対する DoS 攻撃のターゲットとしては単純すぎます

ロックアウトの詳細については、「OWASP による弱いロックアウト メカニズムのテスト」を参照してください。

アプリを失敗したログイン ロックアウトを有効にする Identity に移行する場合は、移行の一環として LockoutEnabled を true に設定する必要があります。

Note

すべてのフィールド マッピングに、同様のメンバーシップから ASP.NET Core Identity への一対一の関係があるとは限りません。 上の表は、メンバーシップの既定のユーザー スキーマを取り出し、それを ASP.NET Core Identity のスキーマにマップしたものです。 メンバーシップで使われていた他のカスタム フィールドは、手動でマップする必要があります。 パスワードの条件とパスワード ソルトのどちらも 2 つの間で移行されないため、このマッピングではパスワードのマップはありません。 パスワードは null のままにしておき、ユーザーにパスワードのリセットを求めることをお勧めします。 ASP.NET Core Identity では、ユーザーがロックアウトされている場合は、LockoutEnd を将来の日付に設定する必要があります。これは移行スクリプトに示されています。

ロール

Identity
(dbo.AspNetRoles) 列
Type Membership
(dbo.aspnet_Roles) 列
Type
Id string RoleId string
Name string RoleName string
NormalizedName string LoweredRoleName string

ユーザー ロール

Identity
(dbo.AspNetUserRoles) 列
Type Membership
(dbo.aspnet_UsersInRoles) 列
Type
RoleId string RoleId string
UserId string UserId string

UsersRoles の移行スクリプトを作るときは、上のマッピング テーブルを参照してください。 次の例では、データベース サーバーに 2 つのデータベースがあるものとします。 1 つのデータベースには、既存の ASP.NET メンバーシップのスキーマとデータが含まれています。 もう 1 つの CoreIdentitySample データベースは、前に説明した手順を使用して作成されたものです。 詳細なコメントが含まれています。

-- THIS SCRIPT NEEDS TO RUN FROM THE CONTEXT OF THE MEMBERSHIP DB
BEGIN TRANSACTION MigrateUsersAndRoles
USE aspnetdb

-- INSERT USERS
INSERT INTO CoreIdentitySample.dbo.AspNetUsers
            (Id,
             UserName,
             NormalizedUserName,
             PasswordHash,
             SecurityStamp,
             EmailConfirmed,
             PhoneNumber,
             PhoneNumberConfirmed,
             TwoFactorEnabled,
             LockoutEnd,
             LockoutEnabled,
             AccessFailedCount,
             Email,
             NormalizedEmail)
SELECT aspnet_Users.UserId,
       aspnet_Users.UserName,
       -- The NormalizedUserName value is upper case in ASP.NET Core Identity
       UPPER(aspnet_Users.UserName),
       -- Creates an empty password since passwords don't map between the 2 schemas
       '',
       /*
        The SecurityStamp token is used to verify the state of an account and
        is subject to change at any time. It should be initialized as a new ID.
       */
       NewID(),
       /*
        EmailConfirmed is set when a new user is created and confirmed via email.
        Users must have this set during migration to reset passwords.
       */
       1,
       aspnet_Users.MobileAlias,
       CASE
         WHEN aspnet_Users.MobileAlias IS NULL THEN 0
         ELSE 1
       END,
       -- 2FA likely wasn't setup in Membership for users, so setting as false.
       0,
       CASE
         -- Setting lockout date to time in the future (1,000 years)
         WHEN aspnet_Membership.IsLockedOut = 1 THEN Dateadd(year, 1000,
                                                     Sysutcdatetime())
         ELSE NULL
       END,
       aspnet_Membership.IsLockedOut,
       /*
        AccessFailedAccount is used to track failed logins. This is stored in
        Membership in multiple columns. Setting to 0 arbitrarily.
       */
       0,
       aspnet_Membership.Email,
       -- The NormalizedEmail value is upper case in ASP.NET Core Identity
       UPPER(aspnet_Membership.Email)
FROM   aspnet_Users
       LEFT OUTER JOIN aspnet_Membership
                    ON aspnet_Membership.ApplicationId =
                       aspnet_Users.ApplicationId
                       AND aspnet_Users.UserId = aspnet_Membership.UserId
       LEFT OUTER JOIN CoreIdentitySample.dbo.AspNetUsers
                    ON aspnet_Membership.UserId = AspNetUsers.Id
WHERE  AspNetUsers.Id IS NULL

-- INSERT ROLES
INSERT INTO CoreIdentitySample.dbo.AspNetRoles(Id, Name)
SELECT RoleId, RoleName
FROM aspnet_Roles;

-- INSERT USER ROLES
INSERT INTO CoreIdentitySample.dbo.AspNetUserRoles(UserId, RoleId)
SELECT UserId, RoleId
FROM aspnet_UsersInRoles;

IF @@ERROR <> 0
  BEGIN
    ROLLBACK TRANSACTION MigrateUsersAndRoles
    RETURN
  END

COMMIT TRANSACTION MigrateUsersAndRoles

前のスクリプトが完了すると、前に作成した ASP.NET Core Identity アプリにメンバーシップのユーザーが設定されます。 ユーザーはログインする前にパスワードを変更する必要があります。

Note

メンバーシップ システムにメールアドレスと一致しないユーザー名を持つユーザーがいる場合は、前に作成されたアプリを変更してこれに対応する必要があります。 既定のテンプレートでは、UserNameEmail が同じであることが想定されています。 それらが異なる場合は、Email の代わりに UserName を使うようにログイン プロセスを変更する必要があります。

ログイン ページの PageModel で、Email プロパティから [EmailAddress] 属性を削除します。 その名前を UserName に変更します。 ViewPageModelEmailAddress が使われているすべての場所で変更が必要です。 結果は次のようになります。

Fixed Login

次のステップ

このチュートリアルでは、SQL メンバーシップから ASP.NET Core 2.0 Identity にユーザーを移植する方法について説明しました。 ASP.NET Core Identity について詳しくは、Identity の概要に関する記事をご覧ください。