ASP.NET Identity 2.0 でプロファイル情報を Claim として ClaimsIdentity オブジェクトに追加し、拡張メソッドを使って ClaimsIdentity オブジェクトからプロファイル情報を取得・表示する方法を書きます。(この記事は .NET Framework 版です。Core 版は別の記事「ASP.NET Core MVC の ClaimsIdentity」に書きましたのでそちらを見てください)
プロファイル情報とは、ユーザーのメールアドレス、電話番号などのユーザー固有の情報です。ASP.NET プロファイル機能は、フォーム認証のためのクレデンシャル情報(ユーザー名とパスワード)と共に、プロファイル情報を個々のユーザーに関連付けてデータベースに格納します。
ASP.NET Identity 2.0 では、プロファイル情報として Email, PhoneNumber が IdentityUser クラスに定義済みです。それらに加えて、CodeZine の記事 ASP.NET Identityのプロファイル情報のカスタマイズにあるように、姓、名、誕生日などの任意の情報を追加することができます。
プロファイル情報の標準的な設定方法や取得方法は CodeZine の記事を読んでいただければわかるのでここでは書きません。(手抜きでスミマセン。上にリンクを張った CodeZine の記事は ASP.NET Identity 1.0 のもので、2.0 のものとは AspNetUsers テーブルの内容などが異なりますが、基本は同じです)
ここでは、CodeZine の記事のようにその都度データベースから情報を取得するのではなく、User.Identity プロパティから取得できる ClaimsIdentity オブジェクトにプロファイル情報を含めておき、それから取得する方法を書きます。
そのような方法を取る理由は、例えば上の画像のようにマスターページの右上に常にユーザー情報を表示するような場合、ページを描画するたびにデータベースにクエリを発行してプロファイル情報を取得するのは負荷が重そうに感じたからです。
ClaimsIdentity オブジェクトにプロファイル情報を含めれば、ユーザー認証後は認証クッキーに含まれたプロファイル情報がクライアントから送信されてきて、それをベースに ClaimsIdentity オブジェクトを再生成するのだと思います。(それが書いてある Microsoft の公式文書が見つからないので想像の域を出ませんが、実際にいろいろ試した結果からその想像は合っていると思います)
であれば、再生成された ClaimsIdentity オブジェクトからプロファイル情報を取得する方が、データベースから取得するより、負荷は軽そうです。(実は気にするほどの差はないのかもしれませんが)
以下に、例として、PhoneNumber という定義済みのプロファイル情報を Claim として ClaimsIdentity へ追加するコード、ClaimsIdentity からプロファイル情報を取得するための拡張メソッドのコードを載せておきます。
ベースは ASP.NET Web Forms の Web アプリケーションプロジェクトを Visual Studoi 2015 Community のテンプレートを使って自動生成した IdentityModel.cs です。それに Claim を追加するコードと拡張メソッドを追加しています。(Web サイトプロジェクトでは、自動生成される IdentityModel.cs がかなり異なり、同じようにできるかどうかは未確認です。)
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using WebFormsApp.Models;
// 拡張メソッドで FirstOrDefault を使うため追加
using System.Linq;
namespace WebFormsApp.Models
{
public class ApplicationUser : IdentityUser
{
public ClaimsIdentity GenerateUserIdentity(
ApplicationUserManager manager)
{
var userIdentity = manager.CreateIdentity(
this, DefaultAuthenticationTypes.ApplicationCookie);
// ここでは例として PhoneNumber を Claim として追加。
// 未登録(DB 上で NULL)の場合 this.PhoneNumber プロ
// パティは null を返す。null の場合は追加しても意味
// がないので追加しない。 (null のまま追加しようとす
// ると Claim コンストラクタで例外がスローされる)
if (!string.IsNullOrEmpty(this.PhoneNumber))
{
// ClaimTypes クラスは System.Security.Claims 名前
// 空間に定義済みなのでそれを利用。PhoneNumber プロ
// パティは IdentityUser クラスに定義済み。
userIdentity.AddClaim(
new Claim(ClaimTypes.HomePhone, this.PhoneNumber));
}
return userIdentity;
}
// ・・・中略・・・
}
// ClaimsIdentity から PhoneNumber を取得する拡張メソッド
// PhoneNumber が Claims にない場合は null を返す。
public static class MyExtensions
{
public static string GetPhoneNumber(
this System.Security.Principal.IIdentity identity)
{
var claimsIdentity = identity as ClaimsIdentity;
if (claimsIdentity != null)
{
var claim = claimsIdentity.Claims.
FirstOrDefault(c => c.Type == ClaimTypes.HomePhone);
if (claim != null)
{
return claim.Value;
}
}
return null;
}
}
// ・・・中略・・・
}
MVC5 アプリでは、テンプレートで自動生成される IdentityModel.cs のコードが上の Web Forms アプリのものとは少々異なりますが、自力で書いて追加する部分のコードは上記と全く同じになります。
上記の拡張メソッドは名前空間をインポートすればスコープの中に取り込むことができます。例えば、上の画像のようにマスターページの右上に表示する場合は以下のようにします。
<%@ Import Namespace="WebFormsApp.Models" %>
<a runat="server" href="~/Account/Manage"
title="Manage your account">
Hello, <%: Context.User.Identity.GetUserName() %> !
Phone: <%: Context.User.Identity.GetPhoneNumber() %>
</a>