WebSurfer's Home

Filter by APML

プロファイル情報を ClaimsIdentity へ追加

by WebSurfer 16. April 2017 14:13

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>

Tags: ,

Authentication

基本認証

by WebSurfer 23. November 2015 14:19

IIS7 で基本認証を行う際のブラウザとサーバーのやり取りを調べましたので備忘録として書いておきます。

基本認証のやり取り

上の画像は IIS7 と IE9 で基本認証を行った場合の要求 / 応答を Fiddler2 でキャプチャしたものです。

  1. 最初の要求に対してはサーバーから応答ヘッダに WWW-Authenticate: Basic realm="xxx" を含む HTTP 401 応答が返ってきます。上の画像の #2 がそれです。xxx には IIS7 の場合ホスト名が入ります。
  2. ブラウザはそれを受けてユーザーに[ユーザー名]と[パスワード]の入力を促すダイアログを表示します。
  3. ユーザーがダイアログに[ユーザー名]と[パスワード]を入力して[OK]ボタンをクリックすると、ブラウザは 1 で要求したページを再度 GET 要求します。上の画像の #3 がそれです。
  4. その際、ブラウザは認証用のヘッダ Authorization: Basic UGFwaWt...(上の画像で赤枠で囲った部分を見てください)をサーバーに送ります。UGFwaWt... の部分は[ユーザ名]と[パスワード]をコロン ":" でつなぎ Base64 でエンコードしたものです。
  5. サーバーはこのヘッダを見てユーザーを認証し、HTTP 200 ステータスコード(成功)と共に要求されたコンテンツをブラウザに送信します。
  6. これ以降、ユーザーがブラウザを閉じない限りホスト名 xxx に対しては認証用のヘッダ(画像で赤枠で囲ったものと同じ)が要求ヘッダに含まれて送信され、要求のたび自動的に認証が行われるようになります。上の画像の #4, #5 がそれです。

以上、基本認証でのブラウザとサーバーのやり取りを簡単にまとめてみました。

フォーム認証と Windows 認証の場合は、それぞれ先の記事「Forms 認証のログイン・ログオフ動作」と「非ドメインユーザーの誘導」に書きましたので興味がありましたら見てください。

Tags: ,

Authentication

パススルー認証

by WebSurfer 13. September 2015 16:29

IIS マネージャーで[サイトの編集]を行う際、下の画像(Vista の IIS7 のものです)のように「パススルー認証」という言葉が出てきますが、これは一体何かという話を書きます。

パススルー認証

まず、そもそもパススルー認証とは一般的にどういう意味かですが広義には以下のようなことらしいです。

例えば、サーバー A とサーバー B の 2 つのサーバーがあり、サーバー B のみにユーザーの資格情報が保持されているとします。

クライアントがサーバー A にアクセスした際、サーバー A ではユーザー認証ができないので、サーバー A はサーバー B にユーザー認証を要求します。そのようなメカニズムをパススルー認証 (Pass-Through Authentication) と呼んでいるそうです。

MSDN の記事 Pass-Through Authentication の Figure 1 を見て、Active Directory ドメインサービス環境で統合 Windows 認証を使用している場合を考えると理解しやすいかもしれません。

で、それと上の画像の「パススルー認証」とどういう関係があるのかですが、それについては「サイトの編集」ダイアログのヘルプ(右上の ? ボタンをクリックすると表示される)に以下の説明があります。

"必要に応じて、[接続] をクリックして、物理パスに接続するための資格情報を指定することもできます。 資格情報を指定しない場合、Web サーバーはパススルー認証を使用します。 これは、コンテンツにはアプリケーション ユーザーの ID を使用してアクセスし、構成ファイルにはアプリケーション プールの ID を使用してアクセスすることを意味します。"

注意 1:
上で「コンテンツにはアプリケーション ユーザーの ID を使用」とありますが、これは統合 Windows 認証の環境でパススルー認証によってユーザー認証が完了した場合で、匿名認証の場合は IUSR が使用されます。また、「コンテンツ」というのは静的コンテンツのみです。(動的コンテンツについては下の注意 3 参照)

注意 2:
上で言う「構成ファイル」とは web.config のことです。物理パス C:\inetpub\wwwroot には自動的にアプリケーションプール ID が適切なアクセス権を持つように設定されます(正確には、Microsoft TechNet の記事 に書いてあるように、wwwroot フォルダに対して必要なアクセス権を持つ IIS_IUSRS グループが設定されます。そして、実行時にアプリケーションプール ID のアクセストークンに対して IIS_IUSRS メンバーシップが自動的に追加されるので web.config には問題なくアクセスできます)。

ただし、物理パスが C:\inetpub\wwwroot 以外にある場合は要注意です。特に、物理パスが UNC にあって web.config も UNC にある場合がややこしいです(詳しくは Microsoft Support の記事 を見てください)。

注意 3:
.aspx, .ascx などの動的コンテンツへのアクセス、.aspx.cs, .ascx.cs などコードビハインドのコードでのファイルや SQL Server へのアクセスは「アプリケーション プールの ID を使用」します。

この場合、先の記事 で書きましたように Temporary ASP.NET Files フォルダーにコンパイル済みアセンブリが置かれますので、アプリケーション プールの ID はそのフォルダに対しても適切なアクセス権を持つ必要があります。(自動的に設定されているはず)

なお、.aspx ページの中に外部スクリプトファイルや外部 CSS ファイルなど静的ファイルを取り込むための定義(例: <script src="/scripts/jquery.js" ...)がされていて、.aspx ページがブラウザに読み込まれた後、ブラウザがそれらの静的ファイルをサーバーに要求した場合は、上で言う「コンテンツにはアプリケーション ユーザーの ID を使用」が当てはまりますので注意してください。ただし、HTTP ハンドラ経由で静的ファイルを取得する場合(例:HTTP ハンドラでキャッシュコントロール)は話は別で「アプリケーション プールの ID を使用」します。

さらに、「サイトの編集」ダイアログ上の [接続] をクリックすると表示される「接続」ダイアログのヘルプには [アプリケーション ユーザー (パススルー認証)] に対して以下の説明がされています。

"このオプションは、パススルー認証を使用する場合に選択します。 このオプションを選択すると、物理パスへのアクセスに要求元のユーザーの資格情報が使用されます。

匿名要求については、匿名認証用に構成されている ID が物理パスへのアクセスに使用されます。 既定では、この ID は組み込みの IUSR アカウントです。

認証された要求の場合、物理パスへのアクセスに要求元のユーザーの認証済み資格情報のセットが使用されます。 このアプリケーションで使用されるアプリケーション プール ID は物理パスに対して読み取りアクセス権を持ち、認証されたユーザーが、物理パス上のコンテンツにアクセスできるようにします。"

・・・という訳で、「接続」ダイアログのパス資格情報で [アプリケーション ユーザー (パススルー認証)] を選択するということは、

  1. 匿名アクセスの場合は IUSR、(デフォルト。IUSR は変更可能
  2. Winsows 認証の場合はログインしたユーザーの Windows アカウント、
  3. Forms 認証でユーザーがログイン済みの場合の場合アプリケーションプール ID(例:IIS7 では NETWORK SERVICE。ASP.NET の ID オブジェクト 参照)

の資格情報で「サイトの編集」ダイアログで [物理パス(P):] に設定したパスのコンテンツにアクセスすることになり、統合 Windows 認証を利用している場合は上に述べたパススルー認証のメカニズムによってユーザーの資格情報をドメインコントローラーから取得してコンテンツにアクセスすることになるはずです。

従って、通常は IIS のデフォルトの設定どおりパススルー認証としておけば、統合 Windows 認証に限らずほとんどのケースで問題なさそうです。

なお、「サイトの設定」ダイアログで [テスト設定(G):] をクリックすると、「テスト接続」ダイアログに [結果(R):] が 2 つ表示され、前者は OK ながら、後者の方に "パス (C:\xxx\yyy) へのアクセスを検証できません。" と表示されて問題ありそうな感じがしますが、それは気にしなくてよさそうです。

テスト接続

前者は ( ) 内に示す ID が有効かどうか、後者はパススルー認証で物理パスへのアクセス権があるかどうかの結果を表示しているようですが、IIS マネージャーが検証できないだけで、実際にアクセス権がないと言っているわけではなさそうですので。

------------

ところで、一番上の画像の「接続」ダイアログで [特定のユーザー(U):] を指定した場合ですが、それついてはまだよく分かってないです。(汗)

物理パスへのアクセスが指定したユーザーのアクセス権で行われるのは間違いなさそうです。

ただし、特定のユーザーを指定することによって、web.config にそのユーザーの偽装が設定されるとか、匿名アクセスの資格情報が IUSR から変更になるということはなかったです。

また、フォーム認証でのログイン操作の際に "System.Data.SqlClient.SqlException: ユーザーにはこの操作を実行する権限がありません。" というエラーが出てログインできなくなるという問題が出るなど、訳がわからないところもあります(App_Data の ASPNETDB.mdf へのユーザーインスタンスへの接続ですが、資格情報の問題ではなさそう。他の DB のユーザーインスタンスには接続できたので)。

今後の課題ということで、分かったらまたこの記事に追記することにします。

Tags: ,

Authentication

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。ブログ2はそれ以外の日々の出来事などのトピックスになっています。

Calendar

<<  December 2025  >>
MoTuWeThFrSaSu
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

View posts in large calendar