次の方法で共有


ASP.NET Identity のカスタム ストレージ プロバイダーの概要

作成者: Tom FitzMacken

ASP.NET Identity は拡張可能なシステムであり、独自のストレージ プロバイダーを作成し、アプリケーションを再作業せずにアプリケーションにプラグインできます。 このトピックでは、ASP.NET Identity 用のカスタマイズされたストレージ プロバイダーを作成する方法について説明します。 独自のストレージ プロバイダーを作成するための重要な概念について説明しますが、カスタム ストレージ プロバイダーを実装する詳細なチュートリアルではありません。

カスタム ストレージ プロバイダーの実装例については、「カスタム MySQL ASP.NET Identity ストレージ プロバイダーの実装」を参照してください。

このトピックは、ASP.NET Identity 2.0 用に更新されました。

チュートリアルで使用するソフトウェアのバージョン

  • Update 2 が適用された Visual Studio 2013
  • ASP.NET Identity 2

はじめに

既定では、ASP.NET Identity システムは SQL Server データベースにユーザー情報を保存し、Entity Framework Code First を使用してデータベースを作成します。 多くのアプリケーションでは、このアプローチが適切に機能します。 ただし、Azure Table Storage など、別の種類の永続化メカニズムを使用したい場合や、既定の実装とは構造が非常に異なるデータベース テーブルが既にある場合があります。 いずれのケースでも、ストレージのメカニズムに合わせてカスタマイズしたプロバイダーを作成し、そのプロバイダーをアプリにプラグインできます。

ASP.NET Identity は、Visual Studio 2013 テンプレートの多くに既定で含まれています。 Microsoft AspNet Identity EntityFramework NuGet パッケージを使用して、ASP.NET Identity の更新プログラムを取得できます。

このトピックのセクションは次のとおりです。

アーキテクチャの概要

ASP.NET Identity は、マネージャーおよびストアと呼ばれるクラスで構成されます。 マネージャーは、アプリケーション開発者が ASP.NET Identity システムでユーザーの作成などの操作を実行するために使用する高レベルのクラスです。 "ストア" は、ユーザーやロールなどのエンティティを永続化する方法を指定する下位レベルのクラスです。 ストアは永続化メカニズムと密接に結び付けられていますが、マネージャーはストアから切り離されているため、アプリケーション全体を中断することなく永続化メカニズムを置き換えることができます。

次の図は、Web アプリとマネージャーの対話と、ストアとデータ アクセス層の対話がどのように行われるかを示しています。

Diagram showing how your web application interacts with the managers

ASP.NET Identity 用のカスタマイズされたストレージ プロバイダーを作成するには、データ ソース、データ アクセス層、このデータ アクセス層と対話するストア クラスを作成する必要があります。 同じマネージャー API を引き続き使用してユーザーに対してデータ操作を実行できますが、これでデータは別のストレージ システムに保存されます。

UserManager または RoleManager の新しいインスタンスを作成するときに、ユーザー クラスの型を指定し、ストア クラスのインスタンスを引数として渡すので、マネージャー クラスをカスタマイズする必要はありません。 この方法により、カスタマイズしたクラスを既存の構造にプラグインできます。 カスタマイズしたストア クラスを使用して UserManager と RoleManager をインスタンス化する方法については、「新しいストレージ プロバイダーを使用するようにアプリケーションを再構成する」のセクションを参照してください。

保存されるデータを理解する

カスタム ストレージ プロバイダーを実装するには、ASP.NET Identity で使用されるデータの型を理解し、アプリケーションに関連する機能を決定する必要があります。

データ​​ 説明
ユーザー Web サイトの登録済みユーザー。 ユーザー ID とユーザー名が含まれます。 ユーザーが (Facebook などの外部サイトからの資格情報を使用するのではなく) サイトに固有の資格情報を使用してログインする場合は、ハッシュされたパスワードと、ユーザーの資格情報で何かが変更されたかどうかを示すセキュリティ スタンプが含まれる場合があります。 さらに、メール アドレス、電話番号、2 要素認証が有効かどうか、失敗したログインの現在の数、アカウントがロックされているかどうかも含まれる場合があります。
ユーザー要求 ユーザーの ID を表す、ユーザーに関するステートメント (またはクレーム) のセット。 ロールで実現できるよりも優れたユーザー ID の表現を実現できます。
ユーザー ログイン ユーザーのログイン時に使用する外部認証プロバイダー (Facebook など) に関する情報。
ロール サイトの認可グループ。 ロール ID とロール名 ("Admin" や "Employee" など) が含まれます。

データ アクセス層を作成する

このトピックは、使用する永続化メカニズムと、そのメカニズムのエンティティを作成する方法を理解していることを前提としています。 このトピックでは、リポジトリまたはデータ アクセス クラスを作成する方法の詳細については説明しません。代わりに、ASP.NET Identity を使用する場合の設計上の判断に関する推奨事項をいくつか示します。

カスタマイズされたストア プロバイダーのリポジトリを設計する際には、高い自由度があります。 必要なのは、アプリケーションで使用する予定の機能のためにリポジトリを作成することだけです。 たとえば、アプリケーションでロールを使用しない場合は、ロールまたはユーザー ロール用のストレージを作成する必要はありません。 お使いのテクノロジと既存のインフラストラクチャのために、ASP.NET Identity の既定の実装とは大きく異なる構造が必要な場合があります。 データ アクセス層では、お使いのリポジトリの構造で動作するロジックを提供します。

ASP.NET Identity 2.0 のデータ リポジトリの MySQL 実装については、「MySQLIdentity.sql」を参照してください。

データ アクセス層では、ASP.NET Identity からデータ ソースにデータを保存するロジックを提供します。 カスタマイズした記憶域プロバイダーのデータ アクセス層には、ユーザーとロールの情報を格納する次のクラスが含まれる場合があります。

クラス 説明
Context 永続化メカニズムに接続してクエリを実行するための情報をカプセル化します。 このクラスは、データ アクセス層の中心です。 他のデータ クラスでは、操作を実行するためにこのクラスのインスタンスが必要になります。 また、このクラスのインスタンスを使用してストア クラスを初期化します。 MySQLDatabase
ユーザー記憶域 ユーザー情報 (ユーザー名やパスワード ハッシュなど) を格納および取得します。 UserTable (MySQL)
ロール記憶域 ロール情報 (ロール名など) を格納および取得します。 RoleTable (MySQL)
UserClaims 記憶域 ユーザー要求情報 (要求の種類や値など) を格納および取得します。 UserClaimsTable (MySQL)
UserLogins 記憶域 ユーザー ログイン情報 (外部認証プロバイダーなど) を格納および取得します。 UserLoginsTable (MySQL)
UserRole 記憶域 ユーザーが割り当てられているロールを保存および取得します。 UserRoleTable (MySQL)

ここでも、アプリケーションで使用するクラスのみを実装する必要があります。

データ アクセス クラスで、特定の永続化メカニズムのデータ操作を実行するコードを指定します。 たとえば、MySQL 実装内では、UserTable クラスに Users データベース テーブルに新しいレコードを挿入するメソッドが含まれています。 _database という名前の変数は、MySQLDatabase クラスのインスタンスです。

public int Insert(TUser user)
{
    string commandText = @"Insert into Users (UserName, Id, PasswordHash, SecurityStamp,Email,EmailConfirmed,PhoneNumber,PhoneNumberConfirmed, AccessFailedCount,LockoutEnabled,LockoutEndDateUtc,TwoFactorEnabled)
        values (@name, @id, @pwdHash, @SecStamp,@email,@emailconfirmed,@phonenumber,@phonenumberconfirmed,@accesscount,@lockoutenabled,@lockoutenddate,@twofactorenabled)";
    Dictionary<string, object> parameters = new Dictionary<string, object>();
    parameters.Add("@name", user.UserName);
    parameters.Add("@id", user.Id);
    parameters.Add("@pwdHash", user.PasswordHash);
    parameters.Add("@SecStamp", user.SecurityStamp);
    parameters.Add("@email", user.Email);
    parameters.Add("@emailconfirmed", user.EmailConfirmed);
    parameters.Add("@phonenumber", user.PhoneNumber);
    parameters.Add("@phonenumberconfirmed", user.PhoneNumberConfirmed);
    parameters.Add("@accesscount", user.AccessFailedCount);
    parameters.Add("@lockoutenabled", user.LockoutEnabled);
    parameters.Add("@lockoutenddate", user.LockoutEndDateUtc);
    parameters.Add("@twofactorenabled", user.TwoFactorEnabled);

    return _database.Execute(commandText, parameters);
}

データ アクセス クラスを作成したら、データ アクセス層内の特定のメソッドを呼び出すストア クラスを作成する必要があります。

ユーザー クラスのカスタマイズ

独自のストレージ プロバイダーを実装するときは、Microsoft.ASP.NET.Identity.EntityFramework 名前空間の IdentityUser クラスと同等のユーザー クラスを作成する必要があります。

次の図は、作成する必要がある IdentityUser クラスと、このクラスに実装するインターフェイスを示しています。

Image of the Identity User class

IUser<TKey> インターフェイスは、要求された操作の実行時に UserManager が呼び出そうとするプロパティを定義します。 インターフェイスには、Id と UserName の 2 つのプロパティが含まれています。 IUser<TKey> インターフェイスを使用すると、汎用の TKey パラメーターを使用してユーザーのキーの型を指定できます。 Id プロパティの型は、TKey パラメーターの値と一致します。

また、キーに文字列値を使用する場合は、Identity フレームワークによって IUser インターフェイス (汎用パラメーターなし) も提供されます。

IdentityUser クラスは IUser を実装し、Web サイト上のユーザーの追加のプロパティまたはコンストラクターを含みます。 次の例は、キーに整数を使用する IdentityUser クラスを示しています。 Id フィールドは、汎用パラメーターの値と一致するように int に設定されます。

public class IdentityUser : IUser<int>
{
    public IdentityUser() { ... }
    public IdentityUser(string userName) { ... }
    public int Id { get; set; }
    public string UserName { get; set; }
    // can also define optional properties such as:
    //    PasswordHash
    //    SecurityStamp
    //    Claims
    //    Logins
    //    Roles
}

完全な実装については、「IdentityUser (MySQL)」を参照してください。

ユーザー ストアのカスタマイズ

ユーザーに対するすべてのデータ操作のメソッドを提供する UserStore クラスも作成します。 このクラスは、Microsoft.ASP.NET.Identity.EntityFramework 名前空間の UserStore<TUser> クラスと同等です。 UserStore クラスでは、IUserStore<TUser, TKey>、および任意のオプション インターフェイスを実装します。 オプションのインターフェイスのうち、どれを実装するかは、アプリケーションで提供する機能に基づいて選択します。

次の図は、作成する必要がある UserStore クラスと関連するインターフェイスを示しています。

Image of the User Store class

Visual Studio の既定のプロジェクト テンプレートには、多くのオプションのインターフェイスがユーザー ストアに実装されていることを前提とするコードが含まれています。 カスタマイズされたユーザー ストアで既定のテンプレートを使用する場合は、ユーザー ストアにオプションのインターフェイスを実装するか、実装していないインターフェイスでメソッドを呼び出さないようにテンプレート コードを変更する必要があります。

次の例は、単純なユーザー ストア クラスを示しています。 TUser 汎用パラメーターは、通常は定義した IdentityUser クラスであるユーザー クラスの型を受け取ります。 TKey 汎用パラメーターは、ユーザー キーの型を受け取ります。

public class UserStore : IUserStore<IdentityUser, int>
{
    public UserStore() { ... }
    public UserStore(ExampleStorage database) { ... }
    public Task CreateAsync(IdentityUser user) { ... }
    public Task DeleteAsync(IdentityUser user) { ... }
    public Task<IdentityUser> FindByIdAsync(int userId) { ... }
    public Task<IdentityUser> FindByNameAsync(string userName) { ... }
    public Task UpdateAsync(IdentityUser user) { ... }
    public void Dispose() { ... }
}

この例では、ExampleDatabase 型の database という名前のパラメーターを受け取るコンストラクターは、データ アクセス クラスを渡す方法を示しているにすぎません。 たとえば、MySQL 実装では、このコンストラクターは MySQLDatabase 型のパラメーターを受け取ります。

UserStore クラス内で、操作を実行するために作成したデータ アクセス クラスを使用します。 たとえば、MySQL 実装の UserStore クラスには、UserTable のインスタンスを使用して新しいレコードを挿入する CreateAsync メソッドがあります。 userTable オブジェクトの Insert メソッドは、前のセクションで示したメソッドと同じです。

public Task CreateAsync(IdentityUser user)
{
    if (user == null) {
        throw new ArgumentNullException("user");
    }

    userTable.Insert(user);

    return Task.FromResult<object>(null);
}

ユーザー ストアをカスタマイズするときに実装するインターフェイス

次の図は、各インターフェイスで定義されている機能の詳細を示しています。 すべての省略可能なインターフェイスは、IUserStore から継承されます。

Illustration showing more details about the functionality defined in each interface

  • IUserStore
    IUserStore<TUser, TKey> インターフェイスは、ユーザー ストアで実装する必要がある唯一のインターフェイスです。 ユーザーの作成、更新、削除、取得を行うためのメソッドを定義します。

  • IUserClaimStore
    IUserClaimStore<TUser, TKey> インターフェイスは、ユーザー クレームを有効にするためにユーザー ストアに実装する必要があるメソッドを定義します。 ユーザー クレームを追加、削除、取得するためのメソッドが含まれています。

  • IUserLoginStore
    IUserLoginStore<TUser, TKey> は、外部認証プロバイダーを有効にするためにユーザー ストアに実装する必要があるメソッドを定義します。 ユーザー ログインを追加、削除、取得するためのメソッドと、ログイン情報に基づいてユーザーを取得するためのメソッドが含まれています。

  • IUserRoleStore
    IUserRoleStore<TKey, TUser> インターフェイスは、ユーザーをロールにマップするためにユーザー ストアに実装する必要があるメソッドを定義します。 ユーザーのロールを追加、削除、取得するメソッドと、ユーザーがロールに割り当てられているかどうかを確認するメソッドが含まれています。

  • IUserPasswordStore
    IUserPasswordStore<TUser, TKey> インターフェイスは、ハッシュされたパスワードを保持するためにユーザー ストアに実装する必要があるメソッドを定義します。 ハッシュされたパスワードを取得および設定するためのメソッドと、ユーザーがパスワードを設定したかどうかを示すメソッドが含まれています。

  • IUserSecurityStampStore
    IUserSecurityStampStore<TUser, TKey> インターフェイスは、ユーザーのアカウント情報が変更されたかどうかを示すセキュリティ スタンプを使用するためにユーザー ストアに実装する必要があるメソッドを定義します。 このスタンプは、ユーザーがパスワードを変更したとき、またはログインを追加または削除したときに更新されます。 セキュリティ スタンプを取得および設定するためのメソッドが含まれています。

  • IUserTwoFactorStore
    IUserTwoFactorStore<TUser, TKey> インターフェイスは、2 要素認証を実装するために実装する必要があるメソッドを定義します。 ユーザーに対して 2 要素認証が有効にされているかどうかを取得および設定するためのメソッドが含まれています。

  • IUserPhoneNumberStore
    IUserPhoneNumberStore<TUser, TKey> インターフェイスは、ユーザーの電話番号を格納するために実装する必要があるメソッドを定義します。 電話番号と、電話番号が確認済みかどうかを取得および設定するためのメソッドが含まれています。

  • IUserEmailStore
    IUserEmailStore<TUser, TKey> インターフェイスは、ユーザーのメール アドレスを格納するために実装する必要があるメソッドを定義します。 メール アドレスと、メールが確認済みかどうかを取得および設定するためのメソッドが含まれています。

  • IUserLockoutStore
    IUserLockoutStore<TUser, TKey> インターフェイスは、アカウントのロックに関する情報を格納するために実装する必要があるメソッドを定義します。 これには、現在の失敗したアクセス試行数の取得、アカウントをロックできるかどうかの取得と設定、ロックアウト終了日の取得と設定、失敗した試行回数の増分、失敗した試行回数のリセットを行うメソッドが含まれています。

  • IQueryableUserStore
    IQueryableUserStore<TUser, TKey> インターフェイスは、クエリ可能なユーザー ストアを提供するために実装する必要があるメンバーを定義します。 これには、クエリ可能なユーザーを保持するプロパティが含まれています。

    アプリケーションで必要なインターフェイスを実装します。次に示すように、IUserClaimStore、IUserLoginStore、IUserRoleStore、IUserPasswordStore、IUserSecurityStampStore のインターフェイスなどです。

public class UserStore : IUserStore<IdentityUser, int>,
                         IUserClaimStore<IdentityUser, int>,
                         IUserLoginStore<IdentityUser, int>,
                         IUserRoleStore<IdentityUser, int>,
                         IUserPasswordStore<IdentityUser, int>,
                         IUserSecurityStampStore<IdentityUser, int>
{
    // interface implementations not shown
}

完全な実装 (すべてのインターフェイスを含む) については、「UserStore (MySQL)」を参照してください。

IdentityUserClaim、IdentityUserLogin、IdentityUserRole

Microsoft.AspNet.Identity.EntityFramework 名前空間には、IdentityUserClaimIdentityUserLoginIdentityUserRole クラスの実装が含まれています。 これらの機能を使用する場合は、これらのクラスの独自のバージョンを作成し、ご自身のアプリケーションのプロパティを定義することができます。 ただし、基本操作 (ユーザーの要求の追加や削除など) を実行するときは、これらのエンティティをメモリに読み込まない方が効率的な場合もあります。 代わりに、バックエンド ストア クラスを使用して、データ ソースに対してこれらの操作を直接実行できます。 たとえば、UserStore.GetClaimsAsync() メソッドは userClaimTable.FindByUserId(user) を呼び出してテーブルに対してクエリを直接実行し、クレームのリストを返すことができます。

public Task<IList<Claim>> GetClaimsAsync(IdentityUser user)
{
    ClaimsIdentity identity = userClaimsTable.FindByUserId(user.Id);
    return Task.FromResult<IList<Claim>>(identity.Claims.ToList());
}

ロール クラスのカスタマイズ

独自のストレージ プロバイダーを実装するときは、Microsoft.ASP.NET.Identity.EntityFramework 名前空間の IdentityRole クラスと同等のロール クラスを作成する必要があります。

次の図は、作成する必要がある IdentityRole クラスと、このクラスに実装するインターフェイスを示しています。

Image of the Identity Role class

IRole<TKey> インターフェイスは、要求された操作の実行時に RoleManager が呼び出そうとするプロパティを定義します。 インターフェイスには、Id と Name の 2 つのプロパティが含まれています。 IRole<TKey> インターフェイスを使用すると、汎用の TKey パラメーターを使用してロールのキーの型を指定できます。 Id プロパティの型は、TKey パラメーターの値と一致します。

また、キーに文字列値を使用する場合は、Identity フレームワークによって IRole インターフェイス (汎用パラメーターなし) も提供されます。

次の例は、キーに整数を使用する IdentityRole クラスを示しています。 Id フィールドは、汎用パラメーターの値と一致するように int に設定されます。

public class IdentityRole : IRole<int>
{
    public IdentityRole() { ... }
    public IdentityRole(string roleName) { ... }
    public int Id { get; set; }
    public string Name { get; set; }
}

完全な実装については、「IdentityRole (MySQL)」を参照してください。

ロール ストアのカスタマイズ

ロールに対するすべてのデータ操作のメソッドを提供する RoleStore クラスも作成します。 このクラスは、Microsoft.ASP.NET.Identity.EntityFramework 名前空間の RoleStore<TRole> クラスと同等です。 RoleStore クラスでは、IRoleStore<TRole, TKey>、および必要に応じて IQueryableRoleStore<TRole, TKey> インターフェイスを実装します。

Image showing a role store class

次の例は、ロール ストア クラスを示しています。 TRole 汎用パラメーターは、通常は定義した IdentityRole クラスであるロール クラスの型を受け取ります。 TKey 汎用パラメーターは、ロール キーの型を受け取ります。

public class RoleStore : IRoleStore<IdentityRole, int>
{
    public RoleStore() { ... }
    public RoleStore(ExampleStorage database) { ... }
    public Task CreateAsync(IdentityRole role) { ... }
    public Task DeleteAsync(IdentityRole role) { ... }
    public Task<IdentityRole> FindByIdAsync(int roleId) { ... }
    public Task<IdentityRole> FindByNameAsync(string roleName) { ... }
    public Task UpdateAsync(IdentityRole role) { ... }
    public void Dispose() { ... }
}
  • IRoleStore<TRole>
    IRoleStore インターフェイスは、ロール ストア クラスで実装するメソッドを定義します。 ロールの作成、更新、削除、取得を行うためのメソッドが含まれています。

  • RoleStore<TRole>
    RoleStore をカスタマイズするには、IRoleStore インターフェイスを実装するクラスを作成します。 システムでロールを使用する場合にのみ、このクラスを実装する必要があります。 ExampleDatabase 型の database という名前のパラメーターを受け取るコンストラクターは、データ アクセス クラスを渡す方法を示しているにすぎません。 たとえば、MySQL 実装では、このコンストラクターは MySQLDatabase 型のパラメーターを受け取ります。

    完全な実装については、「RoleStore (MySQL)」を参照してください。

新しいストレージ プロバイダーを使用するようにアプリを再構成する

新しいストレージ プロバイダーを実装しました。 次に、このストレージ プロバイダーを使用するようにアプリケーションを構成する必要があります。 既定のストレージ プロバイダーがプロジェクトに含まれていた場合は、既定のプロバイダーを削除し、こちらのプロバイダーに置き換える必要があります。

MVC プロジェクトの既定のストレージ プロバイダーを置き換える

  1. [NuGet パッケージの管理] ウィンドウで、Microsoft ASP.NET Identity EntityFramework パッケージをアンインストールします。 このパッケージを見つけるには、インストール済みのパッケージで Identity.EntityFramework を検索します。
    Image of the Nu Get packages window Entity Framework もアンインストールするかどうかを確認するメッセージが表示されます。 アプリケーションの他の部分で不要な場合は、アンインストールできます。

  2. Models フォルダーの IdentityModels.cs ファイルで、ApplicationUser クラスと ApplicationDbContext クラスを削除またはコメント化します。 MVC アプリケーションでは、IdentityModels.cs ファイル全体を削除してかまいません。 Web Forms アプリケーションでは、2 つのクラスを削除しますが、IdentityModels.cs ファイルにも配置されているヘルパー クラスを保持していることを確認します。

  3. ストレージ プロバイダーが別のプロジェクトに存在する場合は、Web アプリケーションにその参照を追加します。

  4. using Microsoft.AspNet.Identity.EntityFramework; へのすべての参照を、記憶域プロバイダーの名前空間の using ステートメントに置き換えます。

  5. Startup.Auth.cs クラスで、適切なコンテキストの 1 つのインスタンスを使用するように ConfigureAuth メソッドを変更します。

    public void ConfigureAuth(IAppBuilder app)
    {
        app.CreatePerOwinContext(ExampleStorageContext.Create);
        app.CreatePerOwinContext(ApplicationUserManager.Create);
        ...
    
  6. App_Start フォルダーで、IdentityConfig.cs を開きます。 ApplicationUserManager クラスで、Create メソッドを変更して、カスタマイズしたユーザー ストアを使用するユーザー マネージャーを返すようにします。

    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) 
    {
        var manager = new ApplicationUserManager(new UserStore(context.Get<ExampleStorageContext>()));
        ...
    }
    
  7. ApplicationUser へのすべての参照を IdentityUser に置き換えます。

  8. 既定のプロジェクトには、IUser インターフェイスで定義されていないユーザー クラスのメンバーがいくつか含まれています。Email、PasswordHash、GenerateUserIdentityAsync などです。 ユーザー クラスにこれらのメンバーがない場合は、それらを実装するか、これらのメンバーを使用するコードを変更する必要があります。

  9. RoleManager のインスタンスを作成している場合は、新しい RoleStore クラスを使用するようにそのコードを変更します。

    var roleManager = new RoleManager<IdentityRole>(new RoleStore(context.Get<ExampleStorageContext>()));
    
  10. 既定のプロジェクトは、キーに文字列値を持つユーザー クラス用に設計されています。 ユーザー クラスのキーの型が異なる場合 (整数など)、その型を操作するようにプロジェクトを変更する必要があります。 「ASP.NET Identity でユーザーの主キーを変更する」を参照してください。

  11. 必要に応じて、接続文字列を Web.config ファイルに追加します。

その他のリソース