次の方法で共有


ユーザー ベースの承認 (VB)

作成者: Scott Mitchell

Note

この記事を作成した後で、ASP.NET メンバーシップ プロバイダーよりも ASP.NET Identity のほうが適切になりました。 この記事の作成時点で紹介されている Membership プロバイダーではなく、ASP.NET Identity プラットフォームを使うようにアプリを更新することを強くお勧めします。 ASP.NET メンバーシップ システムと比べると、ASP.NET Identity には次のような多くの利点があります。

  • パフォーマンスの向上
  • 向上した拡張性とテストの容易性
  • OAuth、OpenID Connect、2 要素認証のサポート
  • クレームベースの ID のサポート
  • ASP.Net Core との相互運用性の向上

コードのダウンロードまたは PDF のダウンロード

このチュートリアルでは、さまざまな手法を使用して、ページへのアクセスを制限し、ページレベルの機能を制限する方法について説明します。

はじめに

ユーザー アカウントを提供するほとんどの Web アプリケーションには、サイト内の特定のページへのアクセスを特定の訪問者に制限する目的もあります。 たとえば、ほとんどのオンライン掲示板サイトでは、すべての匿名ユーザーと認証済みユーザーが掲示板の投稿を閲覧できますが、新しい投稿を作成できるのは認証済みユーザーだけです。 また、特定のユーザー (または特定のユーザー グループ) のみがアクセスできる管理ページがある場合もあります。 さらに、ページレベルの機能は、ユーザーごとに異なる場合があります。 投稿の一覧を表示する際、認証済みのユーザーには各投稿を評価するためのインターフェイスが表示されますが、匿名の訪問者にはこのインターフェイスは表示されません。

ASP.NET を使用すると、ユーザーベースの認可規則を簡単に定義できます。 Web.config に少しマークアップを追加するだけで、特定の Web ページまたはディレクトリ全体をロック ダウンして、指定した一部のユーザーのみがアクセスできるようにすることができます。 ページレベルの機能は、現在ログインしているユーザーに基づいて、プログラムおよび宣言的な手段を使用して有効または無効にすることができます。

このチュートリアルでは、さまざまな手法を使用して、ページへのアクセスを制限し、ページレベルの機能を制限する方法について説明します。 それでは始めましょう。

URL 認可ワークフローの概要

フォーム認証の概要」チュートリアルで説明したように、ASP.NET ランタイムが ASP.NET リソースの要求を処理する際、そのライフサイクル中に要求によって複数のイベントが発生します。 HTTP モジュールは、要求ライフサイクル内の特定のイベントに応答してコードが実行されるマネージド クラスです。 ASP.NET には、バックグラウンドで重要なタスクを実行する多数の HTTP モジュールが付属しています。

そのような HTTP モジュールの 1 つが FormsAuthenticationModule です。 前のチュートリアルで説明したように、FormsAuthenticationModule の主な機能は、現在の要求の ID を決定することです。 これは、Cookie 内にあるか、URL 内に埋め込まれているフォーム認証チケットを検査することによって行われます。 この識別は、AuthenticateRequest イベント中に実行されます。

もう 1 つの重要な HTTP モジュールは UrlAuthorizationModule で、AuthorizeRequest イベント (AuthenticateRequest イベントの後に発生) に応答する形で生成されます。 UrlAuthorizationModule は、Web.config の構成マークアップを検証し、現在の ID に指定したページにアクセスする権限があるかどうかを決定します。 このプロセスは、URL 認可と呼ばれます。

手順 1 では URL 認可規則の構文を検証しますが、まずは要求が認可されているかどうかによって、UrlAuthorizationModule がどのような動作をするかについて見ていきましょう。 UrlAuthorizationModule は要求が認可されていることを確認すると、何の動作も行わず、要求はそのライフサイクルを継続します。 ただし、要求が認可されていない場合、UrlAuthorizationModule はライフサイクルを中止し、Response オブジェクトに HTTP 401 Unauthorized ステータスを返すよう指示します。 フォーム認証を使用する際、FormsAuthenticationModule が HTTP 401 ステータスを検出した場合は、ログイン ページへの HTTP 302 Redirect に変更するため、この HTTP 401 ステータスがクライアントに返されることはありません。

図 1 は、認可されていない要求を受信した場合の ASP.NET パイプライン、FormsAuthenticationModuleUrlAuthorizationModule のワークフローを示しています。 特に、図 1 は、匿名ユーザーへのアクセスを拒否するページである ProtectedPage.aspx に対する匿名の訪問者からの要求を示しています。 訪問者が匿名であるため、UrlAuthorizationModule は要求を中止し、HTTP 401 Unauthorized ステータスを返します。 次に、FormsAuthenticationModule は 401 ステータスを、ログイン ページへの 302 Redirect に変換します。 ユーザーがログイン ページで認証されると、ProtectedPage.aspx にリダイレクトされます。 今回は、FormsAuthenticationModule が認証チケットに基づいてユーザーを識別します。 訪問者が認証されたので、UrlAuthorizationModule はページへのアクセスを許可します。

フォーム認証と URL 承認ワークフロー

図 1: フォーム認証と URL 認可のワークフロー (クリックするとフルサイズの画像が表示されます)

図 1 は、匿名ユーザーが利用できないリソースに匿名の訪問者がアクセスしようとしたときに発生する相互作用を表しています。 このような場合、匿名の訪問者は、訪問しようとしたページがクエリ文字列に指定された状態で、ログイン ページにリダイレクトされます。 ユーザーが正常にログオンすると、自動的に最初に閲覧しようとしていたリソースにリダイレクトされます。

匿名ユーザーが認可されていない要求を行った場合、このワークフローは単純で、訪問者は発生した事象とその理由を簡単に理解できます。 ただし、FormsAuthenticationModule は認証済みのユーザーから要求が行われた場合でも、すべての認可されていないユーザーをログイン ページにリダイレクトすることに注意してください。 認証済みのユーザーが権限を持たないページにアクセスしようとした場合、ユーザーが混乱する可能性があります。

Web サイトで、ASP.NET ページ OnlyTito.aspx に Tito だけがアクセスできるように URL 認可規則が構成されているとします。 次に、Sam がサイトにアクセスし、ログオンした後、OnlyTito.aspx にアクセスを試みたとします。 UrlAuthorizationModule は要求のライフサイクルを停止し、HTTP 401 Unauthorized ステータスを返します。FormsAuthenticationModule はこれを検知し、Sam をログイン ページにリダイレクトします。 ただし、Sam は既にログインしているため、なぜログイン ページに戻されたのか不思議に思うかもしれません。 何らかの理由でログイン資格情報が失われたか、無効な資格情報を入力したのではないかと考えるかもしれません。 Sam がログイン ページから資格情報を再度入力すると、(再び) ログオンに成功し、OnlyTito.aspx にリダイレクトされます。 UrlAuthorizationModule は Sam がこのページにアクセスできないことを検出し、ログイン ページに戻します。

図 2 は、この混乱を招くワークフローを示しています。

既定のワークフローが混乱を招く可能性がある

図 2: 既定のワークフローは混乱を招くサイクルに陥る可能性がある (クリックするとフルサイズの画像が表示されます)

図 2 に示すワークフローは、コンピューターに精通したユーザーでもすぐに混乱してしまう可能性があります。 手順 2 では、このような混乱を招くサイクルを防ぐ方法を見ていきます。

Note

ASP.NET は、現在のユーザーが特定の Web ページにアクセスできるかどうかを確認するために、URL 認可とファイル認可という 2 つのメカニズムを使用しています。 ファイル認可は FileAuthorizationModule によって実装され、要求されたファイルの ACL を参照して権限を決定します。 ファイル認可は Windows 認証と共によく使用されますが、ACL は Windows アカウントに適用されるアクセス許可であることがその理由です。 フォーム認証を使用するときは、サイトにアクセスするユーザーに関係なく、オペレーティング システムレベルとファイル システムレベルのすべての要求が同じ Windows アカウントによって実行されます。 このチュートリアル シリーズではフォーム認証に焦点を当てているため、ファイル認可については説明しません。

URL 認可のスコープ

UrlAuthorizationModule は、ASP.NET ランタイムの一部であるマネージド コードです。 Microsoft のインターネット インフォメーション サービス (IIS) Web サーバーのバージョン 7 より前のバージョンは、IIS の HTTP パイプラインと ASP.NET ランタイムのパイプラインとの間に明確な障壁がありました。 つまり、IIS 6 以前では、ASP.NET の UrlAuthorizationModule は、要求が IIS から ASP.NET ランタイムに委任された場合にのみ実行されます。 既定では、IIS は静的コンテンツ自体 (HTML ページ、CSS、JavaScript、イメージ ファイルなど) を処理し、.aspx.asmx、または .ashx の拡張子を持つページが要求されたときにのみ、ASP.NET ランタイムに要求を渡します。

ただし、IIS 7 では、統合された IIS および ASP.NET パイプラインを使用できます。 いくつかの構成設定によって、すべての要求に対して UrlAuthorizationModule を呼び出すように IIS 7 を設定できます。これは、任意の種類のファイルに対して URL 認可規則を定義できることを意味します。 さらに、IIS 7 には独自の URL 認可エンジンが含まれています。 ASP.NET 統合と IIS 7 のネイティブ URL 認可機能の詳細については、「IIS7 URL 認可について」を参照してください。 ASP.NET と IIS 7 の統合の詳細については、Shahram Khosravi の書籍『Professional IIS 7 and ASP.NET Integrated Programming』(ISBN: 978-0470152539) を入手してください。

簡単に言うと、IIS 7 より前のバージョンでは、URL 認可規則は、ASP.NET ランタイムによって処理されるリソースにのみ適用されます。 ただし、IIS 7 では、IIS のネイティブ URL 認可機能を使用するか、ASP.NET の UrlAuthorizationModule を IIS の HTTP パイプラインに統合することで、すべての要求に対してこの機能を拡張することができます。

Note

ASP.NET の UrlAuthorizationModule と IIS 7 の URL 認可機能では、認可規則の処理方法に微妙ながらも重要な違いがあります。 このチュートリアルでは、IIS 7 の URL 認可機能や、UrlAuthorizationModule と比較した認可規則の解析方法の違いについては説明しません。 これらのトピックの詳細については、MSDN の IIS 7 ドキュメントまたは www.iis.net を参照してください。

手順 1: Web.config での URL 認可規則の定義

UrlAuthorizationModule は、アプリケーションの構成で定義された URL 認可規則に基づいて、特定の ID に対して要求されたリソースへのアクセスを許可するか拒否するかを決定します。 認可規則は、<authorization> 要素に子要素 <allow> および <deny> の形式で記述されます。 子要素 <allow><deny> では、次を指定できます。

  • 特定のユーザー
  • ユーザーのコンマ区切りリスト
  • すべての匿名ユーザー (疑問符 (?) で表記)
  • すべてのユーザー (アスタリスク (*) で表記)

次のマークアップは、URL 認可規則を使用してユーザー Tito と Scott を許可し、それ以外のユーザーをすべて拒否する方法を示しています。

<authorization>
 <allow users="Tito, Scott" />
 <deny users="*" />
</authorization>

<allow> 要素は、許可するユーザー (Tito と Scott) を定義し、<deny> 要素は、すべてのユーザーを拒否するよう指示します。

Note

<allow> および <deny> 要素では、ロールの認可規則も指定することができます。 ロールベースの認可については、今後のチュートリアルで詳しく説明します。

次の設定では、Sam 以外のすべてのユーザー (匿名の訪問者を含む) のアクセスが許可されます。

<authorization>
 <deny users="Sam" />
</authorization>

認証済みのユーザーのみを許可するには、次の構成を使用します。この構成では、すべての匿名ユーザーへのアクセスが拒否されます。

<authorization>
 <deny users="?" />
</authorization>

認可規則は、Web.config<system.web> 要素内で定義され、Web アプリケーション内のすべての ASP.NET リソースに適用されます。 多くの場合、アプリケーションにはセクションごとに異なる認可規則があります。 たとえば、e コマース サイトでは、すべての訪問者が商品の閲覧、商品レビューの確認、カタログの検索などを行うことができます。 ただし、認証されたユーザーのみが、チェックアウトや配送履歴を管理するページにアクセスできます。 さらに、一部のユーザー (サイト管理者など) のみがアクセスできるサイトが存在する場合があります。

ASP.NET を使用すると、サイト内のさまざまなファイルやフォルダーに対して異なる認可規則を簡単に定義することができます。 ルート フォルダーの Web.config ファイルで指定された認可規則は、サイト内のすべての ASP.NET リソースに適用されます。 ただし、<authorization> セクションと共に Web.config を追加することで、特定のフォルダーに対してこれらの既定の認可設定をオーバーライドすることができます。

認証済みのユーザーのみが Membership フォルダー内の ASP.NET ページにアクセスできるように、Web サイトを更新しましょう。 これを実現するには、Membership フォルダーに Web.config ファイルを追加し、その認可設定を匿名ユーザーを拒否するように設定する必要があります。 ソリューション エクスプローラーで Membership フォルダーを右クリックし、コンテキスト メニューから [新しい項目の追加] メニューを選択し、Web.config という名前の新しい Web 構成ファイルを追加します。

メンバーシップ フォルダーに Web.config ファイルを追加する

図 3: Membership フォルダーに Web.config ファイルを追加する (クリックするとフルサイズの画像が表示されます)

この時点で、プロジェクトには 2 つの Web.config ファイル (1 つはルート ディレクトリにあり、もう 1 つは Membership フォルダーにあります) が含まれている必要があります。

アプリケーションに 2 つの Web.config ファイルが含まれるようになりました

図 4: アプリケーションに現在 2 つの Web.config ファイルが含まれている必要がある (クリックするとフルサイズの画像が表示されます)

Membership フォルダー内の構成ファイルを更新し、匿名ユーザーによるアクセスを禁止します。

<?xml version="1.0"?>
<configuration>
 <system.web>
 <authorization>
 <deny users="?" />
 </authorization>
 </system.web>
</configuration>

これですべて完了です。

この変更をテストするには、ブラウザーでホームページにアクセスし、ログアウト済みであることを確認します。ASP.NET アプリケーションの既定の動作ではすべての訪問者が許可され、ルート ディレクトリの Web.config ファイルに対する認可の変更を行っていないため、ルート ディレクトリのファイルに匿名の訪問者としてアクセスすることができます。

左側の列にある [ユーザー アカウントの作成] リンクをクリックします。 この操作により、~/Membership/CreatingUserAccounts.aspx ページに移動します。 Membership フォルダー内の Web.config ファイルが匿名アクセスを禁止する認可規則を定義しているため、UrlAuthorizationModule は要求を中止し、HTTP 401 Unauthorized ステータスを返します。 FormsAuthenticationModule はこれを 302 Redirect ステータスに変更し、ログイン ページに転送します。 アクセスしようとしていたページ (CreatingUserAccounts.aspx) は、ReturnUrl クエリ文字列パラメーターを介してログイン ページに渡されることに注意してください。

URL 承認規則では匿名アクセスが禁止されているため、ログイン ページにリダイレクトされます

図 5: URL 認可規則により匿名アクセスが禁止されているため、ログイン ページにリダイレクトされる (クリックするとフルサイズの画像が表示されます)

正常にログインすると、CreatingUserAccounts.aspx ページにリダイレクトされます。 今回は匿名ユーザーではなくなったため、UrlAuthorizationModule ページへのアクセスが許可されます。

特定の場所への URL 認可規則の適用

Web.config<system.web> セクションで定義された認可設定は、(別の Web.config ファイルでオーバーライドされない限り) そのディレクトリおよびサブディレクトリ内のすべての ASP.NET リソースに適用されます。 ただし、特定の 1、2 ページを除いて、指定されたディレクトリ内のすべての ASP.NET リソースに特定の認可構成を適用する必要がある場合があります。 これを実現するには、Web.config<location> 要素を追加し、異なる認可規則を持つファイルをポイントし、その中に固有の認可規則を定義します。

<location> 要素を使用して特定のリソースの構成設定をオーバーライドする方法を説明するため、Tito だけが CreatingUserAccounts.aspx にアクセスできるように認可設定をカスタマイズしましょう。 これを実現するには、Membership フォルダーの Web.config ファイルに <location> 要素を追加し、マークアップを次のように更新します。

<?xml version="1.0"?>
<configuration>
 <system.web>
 <authorization>
 <deny users="?" />
 </authorization>
 </system.web>

 <location path="CreatingUserAccounts.aspx">
 <system.web>
 <authorization>
 <allow users="Tito" />
 <deny users="*" />
 </authorization>
 </system.web>
 </location>
</configuration>

<system.web> 内の <authorization> 要素は、Membership フォルダーとそのサブフォルダー内の ASP.NET リソースに対する既定の URL 認可規則を定義します。 <location> 要素を使用すると、特定のリソースに対してこれらの規則をオーバーライドすることができます。 上記のマークアップでは、<location> 要素が CreatingUserAccounts.aspx ページを参照し、Tito を許可し、それ以外のユーザーを拒否するような認可規則を指定しています。

この認可の変更をテストするには、まず匿名ユーザーとして Web サイトにアクセスします。 Membership フォルダー内のページ (UserBasedAuthorization.aspx など) にアクセスしようとすると、UrlAuthorizationModule が要求を拒否し、ログイン ページにリダイレクトされます。 たとえば、Scott としてログインした後は、CreatingUserAccounts.aspx除いて Membership フォルダーのすべてのページにアクセスできます。 Tito 以外のユーザーとしてログオンして CreatingUserAccounts.aspx にアクセスしようとすると、認可されていないアクセスが試みられたことになり、ログイン ページにリダイレクトされます。

Note

<location> 要素は、構成の <system.web> 要素の外側に表示される必要があります。 認可設定をオーバーライドするリソースごとに、別の <location> 要素を使用する必要があります。

UrlAuthorizationModule で認可規則を使用してアクセスを許可または拒否する方法の概要

UrlAuthorizationModule は、URL の認可規則を一度に 1 つずつ分析し、最初の規則から順に下方向へ処理していくことで、特定の URL に対して特定の ID を認可するかどうかを決定します。 一致が見つかるとすぐに、一致が <allow> または <deny> のどちらの要素で見つかったかに応じて、そのユーザーのアクセスが許可または拒否されます。 一致が見つからなかった場合は、そのユーザーのアクセスが許可されます。 そのため、アクセスを制限したい場合は、必ず URL 認可構成内の最後の要素として <deny> 要素を使用する必要があります。 <deny> 要素を省略した場合、すべてのユーザーのアクセスが許可されます。

UrlAuthorizationModule が権限を決定する際に使用するプロセスをより深く理解するため、この手順で前に説明した URL 認可規則の例を考えてみましょう。 最初の規則は、Tito と Scott にアクセスを許可する <allow> 要素です。 2 番目の規則は、すべてのユーザーのアクセスを拒否する <deny> 要素です。 匿名ユーザーがアクセスした場合、UrlAuthorizationModule はまず匿名ユーザーが Scott か Tito かを尋ねます。 答えは明らかに “いいえ“ であるため、2 番目の規則に進みます。 匿名はすべてのユーザー セットに含まれていますか? ここでの答えは “はい“ であるため、<deny> 規則が有効になり、訪問者はログイン ページにリダイレクトされます。 同様に、Jisun がアクセスした場合、UrlAuthorizationModule はまず Jisun が Scott か Tito かを尋ねます。 Jisun はどちらでもないため、UrlAuthorizationModule は 2 番目の質問 (Jisun はすべてのユーザー セットに含まれていますか?) に進みます。 このため、Jisun もアクセスが拒否されます。 最後に、Tito がアクセスした場合、UrlAuthorizationModule が提示する最初の質問が “はい“ であるため、Tito のアクセスは許可されます。

UrlAuthorizationModule は認可規則を上から下に処理し、一致した時点で処理を停止するため、具体性が高い規則を、具体的が低い規則よりも前に配置することが重要です。 このため、Jisun と匿名ユーザーを禁止し、その他の認証済みユーザーをすべて許可する認可規則を定義するには、具体性が最も高い規則 (Jisun に影響するルール) から始めて、その他の認証済みユーザーをすべて許可するが匿名ユーザーをすべて拒否するといった、具体性が低いルールへと進みます。 次の URL 認可規則は、まず Jisun を拒否し、次にすべての匿名ユーザーを拒否することで、このポリシーを実装しています。 Jisun 以外の認証済みユーザーのアクセスは許可されます。これは、これらの <deny> ステートメントのいずれにも一致しないためです。

<authorization>
 <deny users="Jisun" />
 <deny users="?" />
</authorization>

手順 2: 認可されていない認証済みユーザーに対するワークフローの修正

このチュートリアルの「URL 認可ワークフローの概要」セクションで前に説明したように、認可されていない要求が発生すると、UrlAuthorizationModule は要求を中止し、HTTP 401 Unauthorized ステータスを返します。 この 401 ステータスは、FormsAuthenticationModule によって 302 Redirect ステータスに変更され、ユーザーをログイン ページに転送します。 このワークフローは、ユーザーが認証済みであっても、認可されていない要求に対して発生します。

認証済みのユーザーをログイン ページに戻すと、すでにシステムにログイン済みであるため、混乱を招く可能性があります。 少し手を加えて、認証済みユーザーが認可されていない要求を行った際に、制限されたページにアクセスしようとしたことを説明するページにリダイレクトすることで、このワークフローを改善することができます。

まず、Web アプリケーションのルート フォルダーに、UnauthorizedAccess.aspx という名前の新しい ASP.NET ページを作成します。Site.master マスター ページとこのページを関連付けることを忘れないでください。 このページを作成したら、LoginContent ContentPlaceHolder を参照するコンテンツ コントロールを削除し、マスター ページの既定のコンテンツが表示されるようにします。 次に、ユーザーが保護されたリソースにアクセスしようとした状況を説明するメッセージを追加します。 このようなメッセージを追加した後、UnauthorizedAccess.aspx ページの宣言型マークアップは次のようになります。

<%@ Page Language="VB" MasterPageFile="~/Site.master" AutoEventWireup="false"
CodeFile="UnauthorizedAccess.aspx.cs" Inherits="UnauthorizedAccess"
Title="Untitled Page" %>

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent"
Runat="Server">
 <h2>Unauthorized Access</h2>
 <p>
 You have attempted to access a page that you are not authorized to view.
 </p>
 <p>
 If you have any questions, please contact the site administrator.
 </p>
</asp:Content>

次に、認証済みユーザーが認可されていない要求を行った場合に、ログイン ページではなく UnauthorizedAccess.aspx ページに転送されるようにワークフローを変更する必要があります。 認可されていない要求をログイン ページにリダイレクトするロジックは、FormsAuthenticationModule クラスのプライベート メソッド内に埋め込まれているため、この動作をカスタマイズすることはできません。 ただし、ログイン ページに独自のロジックを追加し、必要に応じてユーザーを UnauthorizedAccess.aspx にリダイレクトすることはできます。

FormsAuthenticationModule が認可されていない訪問者をログイン ページにリダイレクトする際、要求された認可されていない URL を ReturnUrl という名前のクエリ文字列に追加します。 たとえば、認可されていないユーザーが OnlyTito.aspx にアクセスしようとした場合、FormsAuthenticationModule はそのユーザーを Login.aspx?ReturnUrl=OnlyTito.aspx にリダイレクトします。 そのため、ReturnUrl パラメーターを含むクエリ文字列で認証済みユーザーがログイン ページにアクセスした場合、この認可されていないユーザーは閲覧権限のないページにアクセスしようとしたことがわかります。 このような場合、このユーザーを UnauthorizedAccess.aspx にリダイレクトしたいと考えています。

この機能を実装するには、ログイン ページの Page_Load イベント ハンドラーに次のコードを追加します。

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
 If Not Page.IsPostBack Then
 If Request.IsAuthenticated AndAlso Not String.IsNullOrEmpty(Request.QueryString("ReturnUrl")) Then
 ' This is an unauthorized, authenticated request...
 Response.Redirect("~/UnauthorizedAccess.aspx")
 End If
 End If
End Sub

上記のコードは、認証済みで認可されていないユーザーを UnauthorizedAccess.aspx ページにリダイレクトします。 このロジックの動作を確認するには、匿名の訪問者としてサイトにアクセスし、左側の列にある [ユーザー アカウントの作成] リンクをクリックします。 これにより、手順 1 で Tito だけにアクセスを許可するように設定した ~/Membership/CreatingUserAccounts.aspx ページに移動します。 匿名ユーザーのアクセスは禁止されているため、FormsAuthenticationModule はログイン ページにリダイレクトします。

この時点では匿名ユーザーであるため、Request.IsAuthenticatedFalse を返し、UnauthorizedAccess.aspx にはリダイレクトされません。 代わりに、ログイン ページが表示されます。 Tito 以外のユーザー (Bruce など) としてログインします。 適切な資格情報を入力すると、ログイン ページから ~/Membership/CreatingUserAccounts.aspx にリダイレクトされます。 ただし、このページは Tito しかアクセスできないため、このページの閲覧は認可されておらず、すぐにログイン ページに戻されます。 ただし、今回 Request.IsAuthenticatedTrue を返す (ReturnUrl クエリ文字列パラメーターも存在する) ため、UnauthorizedAccess.aspx ページにリダイレクトされます。

認証され、承認されていないユーザーがUnauthorizedAccess.aspxにリダイレクトされる

図 6: 認証済みで認可されていないユーザーは UnauthorizedAccess.aspx にリダイレクトされる (クリックするとフルサイズの画像が表示されます)

このカスタマイズされたワークフローは、図 2 に示すサイクルをショートカットして、より理にかなったわかりやすいユーザー エクスペリエンスを提供します。

手順 3: 現在ログイン中のユーザーに基づいた機能の制限

URL 認可により、大まかな認可規則を簡単に指定できます。 手順 1 で説明したように、URL 認可では、フォルダー内の特定のページまたはすべてのページの表示を許可する ID と拒否する ID を簡潔に指定することができます。 ただし、特定のシナリオでは、ページへのアクセスはすべてのユーザーに許可するが、アクセスしているユーザーに基づいてページの機能を制限したい場合があります。

認証済みの訪問者が商品をレビューできる e コマース サイトの例を考えてみましょう。 匿名ユーザーが商品のページにアクセスした場合、商品情報のみが表示され、レビューを投稿することはできません。 ただし、認証済みのユーザーが同じページにアクセスした場合は、レビュー投稿用のインターフェイスが表示されます。 認証済みのユーザーがまだこの商品のレビューを書いていない場合、レビューを投稿できるインターフェイスが表示されます。レビューを投稿済みであれば、以前に投稿したレビューが表示されます。 このシナリオをさらに発展させると、e コマース企業で働くユーザーの商品ページに追加情報を表示し、拡張機能を提供することもできます。 たとえば、従業員が商品ページにアクセスした場合に、在庫の一覧を表示したり、商品の価格と説明文を編集するオプションを提供したりすることもできます。

このようなきめ細かい認可規則は、宣言的にまたはプログラムで (あるいは、この 2 つの何らかの組み合わせを使用して) 実装できます。 次のセクションでは、LoginView コントロールを使用して、きめ細かい認可を実装する方法について説明します。 その後、プログラムによる手法について調べます。 ただし、きめ細かい認可規則の適用を確認するには、最初に、アクセスしているユーザーによって異なる機能を持つページを作成する必要があります。

GridView 内の特定のディレクトリ内のファイルを一覧表示するページを作成しましょう。 各ファイルの名前、サイズ、その他の情報を一覧表示すると共に、GridView には [表示] と [削除] というタイトルの 2 つの列の LinkButton が含まれます。 [表示] LinkButton をクリックすると、選択したファイルのコンテンツが表示されます。[削除] LinkButton をクリックすると、ファイルが削除されます。 最初に、このページを作成して、そのビューと削除の機能をすべてのユーザーが使用できるようにしましょう。 「LoginView コントロールの使用」と「プログラムによる機能の制限」のセクションでは、ページにアクセスしているユーザーに基づいて、これらの機能を有効または無効にする方法について説明します。

Note

これから作成する ASP.NET ページでは、GridView コントロールを使用してファイルの一覧を表示します。 このチュートリアル シリーズでは、フォーム認証、認可、ユーザー アカウント、ロールに重点を置いているため、GridView コントロールの内部動作についてあまり時間を費やしたくありません。 このチュートリアルでは、このページを設定するための具体的な詳しい手順を提供しますが、特定の選択が行われた理由や、特定のプロパティがレンダリングされる出力に与える影響の詳細については詳しく説明しません。 GridView コントロールの詳細な確認については、「ASP.NET 2.0 でのデータの操作」のチュートリアル シリーズを参照してください。

まず、Membership フォルダー内の UserBasedAuthorization.aspx ファイルを開き、FilesGrid という名前のページに GridView コントロールを追加します。 GridView のスマート タグから、[列の編集] リンクをクリックして [フィールド] ダイアログ ボックスを起動します。 ここから、左下隅にある [フィールドの自動生成] チェックボックスをオフにします。 次に、左上隅から [選択] ボタン、[削除] ボタン、2 つの BoundField を追加します ([選択] ボタンと [削除] ボタンは [CommandField] 型の下にあります)。 [選択] ボタンの SelectText プロパティを [表示] に、最初の BoundField の HeaderText および DataField プロパティを [名前] に設定します。 2 番目の BoundField の HeaderText プロパティを [サイズ (バイト単位)]、DataField プロパティを [長さ]、DataFormatString プロパティを {0:N0}、HtmlEncode プロパティを [False] に設定します。

GridView の列を構成したら、[OK] をクリックして [フィールド] ダイアログ ボックスを閉じます。 プロパティ ウィンドウから、GridView の DataKeyNames プロパティを FullName に設定します。 この時点で、GridView の宣言型マークアップは次のようになります。

<asp:GridView ID="FilesGrid" DataKeyNames="FullName" runat="server" AutoGenerateColumns="False">
 <Columns>
 <asp:CommandField SelectText="View" ShowSelectButton="True"/>
 <asp:CommandField ShowDeleteButton="True" />
 <asp:BoundField DataField="Name" HeaderText="Name" />
 <asp:BoundField DataField="Length" DataFormatString="{0:N0}"
 HeaderText="Size in Bytes" HtmlEncode="False" />
 </Columns>
</asp:GridView>

GridView のマークアップを作成したら、特定のディレクトリ内のファイルを取得して GridView にバインドするコードを記述する準備ができました。 ページの Page_Load イベント ハンドラーに次のコードを追加します。

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
 If Not Page.IsPostBack Then
 Dim appPath As String = Request.PhysicalApplicationPath
 Dim dirInfo As New DirectoryInfo(appPath)

 Dim files() As FileInfo = dirInfo.GetFiles()

 FilesGrid.DataSource = files
 FilesGrid.DataBind()
 End If
End Sub

上記のコードでは、DirectoryInfo クラスを使用して、アプリケーションのルート フォルダー内のファイルの一覧を取得します。 GetFiles() メソッドは、ディレクトリ内のすべてのファイルをFileInfo オブジェクトの配列として返し、それを GridView にバインドします。 FileInfo オブジェクトには、NameLengthIsReadOnly などのさまざまなプロパティが含まれています。 宣言型マークアップからわかるように、GridView には Name および Length プロパティだけが表示されます。

Note

DirectoryInfo および FileInfo クラスは System.IO 名前空間にあります。 そのため、これらのクラス名の前に名前空間名を付けるか、名前空間をクラス ファイルにインポートする必要があります (Imports System.IO 経由)。

少し時間を取り、ブラウザーでこのページにアクセスしてみてください。 アプリケーションのルート ディレクトリに存在するファイルの一覧が表示されます。 [表示] または [削除] LinkButton をクリックすると、ポストバックが発生しますが、必要なイベント ハンドラーがまだ作成されていないため、何も処理されません。

GridView は、Web アプリケーションのルート ディレクトリ内のファイルを一覧表示します。

図 7: GridView は Web アプリケーションのルート ディレクトリ内のファイルを一覧表示する (クリックするとフルサイズの画像が表示されます)

選択したファイルのコンテンツを表示する手段が必要です。 Visual Studio に戻り、GridView の上に FileContents という名前の TextBox を追加します。 TextMode プロパティを MultiLine に、Columns および Rows プロパティをそれぞれ 95% および 10 に設定します。

<asp:TextBox ID="FileContents" runat="server" Rows="10"
TextMode="MultiLine" Width="95%"></asp:TextBox>

次に、GridView の SelectedIndexChanged イベントのイベント ハンドラーを作成し、次のコードを追加します。

Protected Sub FilesGrid_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles FilesGrid.SelectedIndexChanged
 ' Open the file and display it
 Dim fullFileName As String = FilesGrid.SelectedValue.ToString()
 Dim contents As String = File.ReadAllText(fullFileName)
 FileContents.Text = contents
End Sub

このコードは、GridView の SelectedValue プロパティを使用して、選択したファイルの完全なファイル名を決定します。 内部的には、SelectedValue を取得するために DataKeys コレクションを参照するため、この手順で前述したように、GridView の DataKeyNames プロパティを [名前] に設定する必要があります。 File クラスは、選択したファイルのコンテンツを文字列として読み取り、それを FileContents TextBox の Text プロパティに割り当て、選択したファイルのコンテンツをページに表示するために使用されます。

選択したファイルの内容がテキスト ボックスに表示されます

図 8: 選択したファイルのコンテンツが TextBox に表示される (クリックするとフルサイズの画像が表示されます)

Note

HTML マークアップを含むファイルのコンテンツを表示し、そのファイルを表示または削除しようとすると、HttpRequestValidationException エラーが発生します。 これは、ポストバック時に TextBox のコンテンツが Web サーバーに返送されるために発生します。 既定では、HTML マークアップなどの潜在的に危険なポストバック コンテンツが検出されるたびに、ASP.NET で HttpRequestValidationException エラーが発生します。 このエラーが発生しないようにするには、@Page ディレクティブに ValidateRequest="false" を追加して、ページの要求検証をオフにします。 要求検証の利点や、無効化する際に実行する必要がある予防措置の詳細については、「要求の検証 - スクリプト攻撃の防止」を参照してください。

最後に、GridView の RowDeleting イベントに次のコードを含むイベント ハンドラーを追加します。

Protected Sub FilesGrid_RowDeleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs)Handles FilesGrid.RowDeleting
 Dim fullFileName As String = FilesGrid.DataKeys(e.RowIndex).Value.ToString()
 FileContents.Text = String.Format("You have opted to delete {0}.", fullFileName)

 ' To actually delete the file, uncomment the following line
 ' File.Delete(fullFileName)
End Sub

このコードは、削除するファイルの完全な名前を FileContents TextBox に表示するだけで、実際にファイルを削除することはありません

[削除] ボタンをクリックしても、ファイルが実際に削除されない

図 9: 削除ボタンをクリックしてもファイルが実際に削除されない (クリックするとフルサイズの画像が表示されます)

手順 1 では、匿名ユーザーが Membership フォルダー内のページを表示できないように、URL 認可規則を構成しました。 きめ細かい認証を適切に実施するため、匿名ユーザーに UserBasedAuthorization.aspx ページへのアクセスを許可しますが、機能には制限を設けます。 このページを開いて、すべてのユーザーがアクセスできるようにするには、Membership フォルダー内の Web.config ファイルに次の <location> 要素を追加します。

<location path="UserBasedAuthorization.aspx">
 <system.web>
 <authorization>
 <allow users="*" />
 </authorization>
 </system.web>
</location>

この <location> 要素を追加した後、サイトからログアウトして新しい URL 認可規則をテストします。 匿名ユーザーとして、UserBasedAuthorization.aspx ページへのアクセスが許可されているはずです。

現時点で、すべての認証済みユーザーまたは匿名ユーザーは UserBasedAuthorization.aspx ページにアクセスし、ファイルを表示または削除できます。 認証済みのユーザーだけがファイルのコンテンツを表示でき、Tito だけがファイルを削除できるように設定しましょう。 このようなきめ細かい認可規則は、宣言的、プログラム、またはその両方の方法を組み合わせて適用できます。 宣言型のアプローチを使用して、ファイルのコンテンツを表示できるユーザーを制限し、プログラムによるアプローチを使用して、ファイルを削除できるユーザーを制限します。

LoginView コントロールの使用

以前のチュートリアルで見てきたように、LoginView コントロールは、認証済みユーザーと匿名ユーザーに異なるインターフェイスを表示するのに役立ち、匿名ユーザーがアクセスできない機能を簡単に非表示にすることができます。 匿名ユーザーはファイルを表示または削除できないため、認証済みユーザーがページにアクセスしたときだけ FileContents TextBox を表示する必要があります。 これを実現するには、ページに LoginView コントロールを追加し、LoginViewForFileContentsTextBox という名前を付けて、FileContents TextBox の宣言的マークアップを LoginView コントロールの LoggedInTemplate に移動します。

<asp:LoginView ID=" LoginViewForFileContentsTextBox " runat="server">
 <LoggedInTemplate>
 <p>
 <asp:TextBox ID="FileContents" runat="server" Rows="10"
 TextMode="MultiLine" Width="95%"></asp:TextBox>
 </p>
 </LoggedInTemplate>
</asp:LoginView>

分離コード クラスから LoginView のテンプレート内の Web コントロールに直接アクセスできなくなりました。 たとえば、FilesGrid GridView の SelectedIndexChanged および RowDeleting イベント ハンドラーは現在 FileContents TextBox コントロールを参照しており、次のようなコードを使用しています。

FileContents.Text = text

ただし、このコードは無効になっています。 FileContents TextBox を LoggedInTemplate に移動すると、TextBox に直接アクセスできなくなります。 代わりに FindControl("controlId") メソッドを使用して、プログラムでコントロールを参照する必要があります。 FilesGrid イベント ハンドラーを次のように更新し、TextBox を参照するようにします。

Dim FileContentsTextBox As TextBox = CType(LoginViewForFileContentsTextBox.FindControl("FileContents"),TextBox)
FileContentsTextBox.Text = text

TextBox を LoginView の LoggedInTemplate に移動し、FindControl("controlId") パターンを使用して TextBox を参照するようにページのコードを更新した後、匿名ユーザーとしてページにアクセスします。 図 10 に示すように、FileContents TextBox は表示されません。 ただし、[表示] LinkButton は表示されたままです。

LoginView コントロールは、認証されたユーザーの FileContents TextBox のみをレンダリングします

図 10: LoginView コントロールは認証済みユーザーの FileContents TextBox のみをレンダリングする (クリックするとフルサイズの画像が表示されます)

匿名ユーザーの [表示] ボタンを非表示にする方法の 1 つは、GridView フィールドを TemplateField に変換することです。 これにより、[表示] LinkButton の宣言型マークアップを含むテンプレートが生成されます。 次に、LoginView コントロールを TemplateField に追加し、LinkButton を LoginView の LoggedInTemplate 内に配置して、匿名ユーザーに対して [表示] ボタンを非表示にすることができます。 これを実現するには、GridView のスマート タグから [列の編集] リンクをクリックして [フィールド] ダイアログ ボックスを開きます。 次に、左下隅の一覧から [選択] ボタンを選択し、[このフィールドを TemplateField に変換する] リンクをクリックします。 これにより、フィールドの宣言型マークアップが次のように変更されます。

<asp:CommandField SelectText="View" ShowSelectButton="True"/>

移動先:

<asp:TemplateField ShowHeader="False">
 <ItemTemplate>
 <asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False"
 CommandName="Select" Text="View"></asp:LinkButton>
 </ItemTemplate>
</asp:TemplateField>

この時点で、LoginView を TemplateField に追加できます。 次のマークアップは、認証済みのユーザーのみに [表示] LinkButton を表示します。

<asp:TemplateField ShowHeader="False">
 <ItemTemplate>
 <asp:LoginView ID="LoginView1" runat="server">
 <LoggedInTemplate>
 <asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="False"
 CommandName="Select" Text="View"></asp:LinkButton>
 </LoggedInTemplate>
 </asp:LoginView>
 </ItemTemplate>
</asp:TemplateField>

図 11 に示すように、[表示] 列が非表示になっているにもかかわらず、[表示] LinkButton が表示されたままになっているため、最終的な結果はあまり美しくありません。 次のセクションでは、(LinkButton だけでなく) GridView 列全体を非表示にする方法について説明します。

LoginView コントロールは、匿名の訪問者の表示 LinkButtons を非表示にします。

図 11: LoginView コントロールによって匿名の訪問者向けの [表示] LinkButton が非表示になる (クリックするとフルサイズの画像が表示されます)

プログラムによる機能の制限

状況によっては、宣言的な手法では、ページの機能を制限するのに不十分です。 たとえば、特定のページ機能を利用できるかどうかは、そのページにアクセスするユーザーが匿名か認証済みかという条件以外の要因にも左右される場合があります。 このような場合、プログラムによる方法で、さまざまなユーザー インターフェイス要素を表示または非表示にすることができます。

プログラムで機能を制限するには、次の 2 つのタスクを実行する必要があります。

  1. ページにアクセスするユーザーが機能にアクセスできるかどうかを確認し、
  2. ユーザーが当該機能にアクセスできるかどうかに基づいて、ユーザー インターフェイスをプログラムで変更します。

この 2 つのタスクの適用例を示すため、Tito に GridView からのファイルの削除のみを許可しましょう。 最初のタスクは、ページにアクセスしているのが Tito であるかどうかを確認することです。 確認できたら、GridView の [削除] 列を非表示 (または表示) にする必要があります。 GridView の列には Columns プロパティを使用してアクセスできます。列は、Visible プロパティが True (既定値) に設定されている場合にのみレンダリングされます。

データを GridView にバインドする前に、Page_Load イベント ハンドラーに次のコードを追加します。

' Is this Tito visiting the page?
Dim userName As String = User.Identity.Name
If String.Compare(userName, "Tito", True) = 0 Then
 ' This is Tito, SHOW the Delete column
 FilesGrid.Columns(1).Visible = True
Else
 ' This is NOT Tito, HIDE the Delete column
 FilesGrid.Columns(1).Visible = False
End If

フォーム認証の概要」チュートリアルで説明したように、User.Identity.Name は ID 名を返します。 これは、Login コントロールに入力されたユーザー名に対応します。 ページにアクセスしているのが Tito の場合、GridView の 2 番目の列の Visible プロパティは True に設定され、そうでない場合は False に設定されます。 最終的な結果として、Tito 以外のユーザーがページにアクセスした場合、認証済みユーザーまたは匿名ユーザーのいずれであっても、[削除] 列はレンダリングされません (図 12 を参照)。ただし、Tito がページにアクセスした場合は [削除] 列が表示されます (図 13 を参照)。

削除列は、Tito 以外のユーザー (Bruce など) が訪問したときにレンダリングされません。

図 12: Tito 以外のユーザー (Bruce など) がページにアクセスしたときに [削除] 列はレンダリングされない (クリックするとフルサイズの画像が表示されます)

列の削除が Tito に対してレンダリングされる

図 13: Tito の場合は [削除] ボタンがレンダリングされる (クリックするとフルサイズの画像が表示されます)

手順 4: 認可規則のクラスとメソッドへの適用

手順 3 では、匿名ユーザーによるファイルのコンテンツの閲覧を禁止し、Tito 以外のすべてのユーザーによるファイルの削除を禁止しました。 これは、宣言的およびプログラムによる手法により、認可されていない訪問者に対して関連するユーザー インターフェイス要素を非表示にすることで実現しました。 この単純な例では、ユーザー インターフェイス要素を適切に非表示にするのは簡単でしたが、同じ機能を実行する方法が数多くあるような、より複雑なサイトの場合はどうでしょうか。 その機能を認可されていないユーザーに制限する場合、該当するユーザー インターフェイス要素をすべて非表示または無効にすることを忘れてしまったら、どうなるでしょうか。

認可されていないユーザーが特定の機能に絶対にアクセスできないようにするための簡単な方法は、そのクラスまたはメソッドを PrincipalPermission 属性で修飾することです。 .NET ランタイムはクラスを使用したり、そのメソッドのうちの 1 つを実行したりする場合、現在のセキュリティ コンテキストにクラスの使用やメソッドの実行を行うアクセス許可があることをチェックして確認します。 PrincipalPermission 属性は、これらの規則を定義できるメカニズムを提供します。

GridView のSelectedIndexChanged および RowDeleting イベント ハンドラーで PrincipalPermission 属性を使用して、匿名ユーザーと Tito 以外のユーザーのそれぞれによる実行を禁止する方法を示しましょう。 そのために必要なのは、各関数定義の上に適切な属性を追加することだけです。

<PrincipalPermission(SecurityAction.Demand, Authenticated:=True)> _
Protected Sub FilesGrid_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles FilesGrid.SelectedIndexChanged
 ...
End Sub

<PrincipalPermission(SecurityAction.Demand, Name:="Tito")> _
Protected Sub FilesGrid_RowDeleting(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewDeleteEventArgs) Handles FilesGrid.RowDeleting
 ...
End Sub

SelectedIndexChanged イベント ハンドラーの属性は、認証済みのユーザーだけがイベント ハンドラーを実行できることを指定するのに対して、RowDeleting イベント ハンドラーの属性は、その実行を Tito に制限します。

Note

属性は、クラス、メソッド、プロパティ、またはイベントに適用できます。 属性を追加する際は、クラス、メソッド、プロパティ、またはイベント宣言ステートメントの一部である必要があります。 Visual Basic ではステートメント区切り記号として改行が使用されるため、属性は宣言と同じ行に、または連結文字 (アンダースコア) を使用してそのすぐ上にある必要があります。 上記のコード スニペットでは、行連結文字を使用して、1 つの行に属性を、そして別の行にメソッド宣言を配置しています。

何らかの方法で Tito 以外のユーザーが RowDeleting イベント ハンドラーを実行しようとするか、認証されていないユーザーが SelectedIndexChanged イベント ハンドラーを実行しようとすると、.NET ランタイムによって SecurityException が発生します。

セキュリティ コンテキストがメソッドの実行を承認されていない場合は、SecurityException がスローされます

図 14: セキュリティ コンテキストがメソッドの実行を認可されていない場合は、SecurityException がスローされる (クリックするとフルサイズの画像が表示されます)

Note

複数のセキュリティ コンテキストからクラスまたはメソッドにアクセスできるようにするには、各セキュリティ コンテキストの PrincipalPermission 属性でクラスまたはメソッドを装飾します。 つまり、Tito と Bruce の両方が RowDeleting イベント ハンドラーを実行できるようにするには、次の属性 追加 PrincipalPermission

<PrincipalPermission(SecurityAction.Demand, Name:="Tito")> _

<PrincipalPermission(SecurityAction.Demand, Name:="Bruce")> _

ASP.NET ページに加えて、多くのアプリケーションには、さまざまなレイヤー (ビジネス ロジック レイヤーやデータ アクセス レイヤーなど) を含むアーキテクチャもあります。 これらのレイヤーは通常、クラス ライブラリとして実装され、ビジネス ロジックやデータ関連の機能を実行するためのクラスとメソッドを提供します。 PrincipalPermission 属性は、これらのレイヤーに認可規則を適用するのに役立ちます。

PrincipalPermission 属性を使用してクラスやメソッドで認可規則を定義する方法の詳細については、Scott GuthriePrincipalPermissionAttributes を使用したビジネスおよびデータ レイヤーへの認可規則の追加に関するブログ エントリを参照してください。

まとめ

このチュートリアルでは、ユーザー ベースの認可規則を適用する方法について説明しました。 まず、ASP.NET の URL 認可フレームワークについて説明しました。 各要求で、ASP.NET エンジンの UrlAuthorizationModule は、アプリケーションの構成で定義された URL 認可規則を調べて、要求されたリソースへのアクセスが ID に認可されているかどうかを確認します。 つまり、URL 認可により、特定のページまたは特定のディレクトリ内のすべてのページに対して認可規則を簡単に指定することができます。

URL 認可フレームワークは、ページ単位で認可規則を適用します。 URL 認可では、要求元の ID に特定のリソースへのアクセスを認可されているかいないかのどちらかを示します。 ただし、多くのシナリオでは、よりきめ細かい認可規則が必要です。 ページへのアクセスを許可するユーザーを定義するのではなく、ページへのアクセスをすべてのユーザーに許可し、ページにアクセスしたユーザーに応じて異なるデータを表示したり、異なる機能を提供したりする必要がある場合もあります。 通常、ページレベルの認可では、特定のユーザー インターフェイス要素を非表示にして、認可されていないユーザーが禁止された機能にアクセスできないようにします。 さらに、属性を使用して、特定のユーザーに対してクラスへのアクセスやメソッドの実行を制限することもできます。

プログラミングに満足!

もっと読む

この記事で説明したトピックの詳細については、次のリソースを参照してください。

作成者について

複数の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジに取り組んでいます。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 Scott には、mitchell@4guysfromrolla.com または http://ScottOnWriting.NET のブログを介して連絡できます。

特別な感謝

このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 今後の MSDN の記事を確認することに関心がありますか? その場合は、mitchell@4GuysFromRolla.com までご一報ください。