[.NET Framework 3.5 セキュリティ/認証あれこれ] クライアントアプリケーションサービス
環境:
Visual Studio 2008
こんにちは。
今日は、クライアントアプリケーションサービスについて記載します。
ASP.NET では、Windows 統合認証以外に、独自なユーザID, パスワード管理をおこなうことができるプロバイダー(メンバーシッププロバイダーなど)を使用したフォーム認証と呼ばれる仕組みが存在しています。
一方、WPF や、Windows フォームクライアントではこうした仕組みの実装は独自にすべての仕組みを構築する必要がありました。
.NET Framework 3.5 からは、クライアントアプリケーションサービスと仕組みによって、この実装のためのフレームワークが追加で提供されています。
さっそく、シンプルなデモでその仕組みをみていきます。
【サービス側の作成】
この仕組みを使用するには、まず、メンバーシップやロールの情報などを公開するサービスを構築します。そして、そのサービスを呼び出してクライアントアプリケーションサービスを使用するクライアントを実装するという手順になります。
まずは、サービス側を実装していきます。
- Visual Studio 2008 で、[Visual C#] - [Web] - [ASP.NET Web サービスアプリケーション] のプロジェクトを新規作成します。
(以降、このプロジェクト名を「AppServices」とします)
- このサービスアプリケーションに、使用するメンバーシッププロバイダーとして、TestMembershipProvider というカスタムのプロバイダークラスを追加します。
まずは、このプロバイダーを使うことを宣言するため、web.config の <system.web> の中に以下を追加します。
<membership defaultProvider="TestMembershipProvider">
<providers>
<add name="TestMembershipProvider" type="AppServices.TestMembershipProvider"/>
</providers>
</membership>
上記で、type 属性では、このメンバーシッププロバイダーの実装として TestMembershipProvider クラスというクラスを使用することを宣言しています。このクラスは、この後で実装をおこなっていきます。
- web.config を以下の通り変更します
[変更前]
<authentication mode="Windows" />
[変更後]
<authentication mode="Forms" />
- <system.web> の前に以下を追加します
<system.web.extensions>
<scripting>
<webServices>
<authenticationService enabled="true" requireSSL = "false"/>
</webServices>
</scripting>
</system.web.extensions>
- では、上記の testMembershipProvider クラスを実装していきます。
ソリューションエクスプローラからプロジェクトを右クリックし、[追加] - [新しい項目] で [クラス] を選択して、TestMembershipProvider.cs としてクラスを新規追加します。(下記の通り、カスタムプロバイダーは MembershipProvider クラスを継承させます。)
public class TestMembershipProvider : MembershipProvider
さらに、コードエディタ上で「MembershipProcider」を選択して右クリックし、コントロールメニューから [抽象クラスの実装] を選択すると、実装が必要なメソッドが一式追加されます。
- では、実際に認証をおこなうメソッドを実装していきます。
上記で追加されたメソッドの中に、ValidateUser というクラスが存在します。今回のサンプルは、簡単な認証の確認をするだけですので、生成された ValidateUser メソッドに、以下の通り実装しましょう。
public override bool ValidateUser(string username, string password)
{
if (username == "test" && password == "test0")
return true;
return false;
}
これでサービス側は完成です。
このサービスが、いつも同じポート番号で起動するように、プロジェクトのプロパティ画面で [Web] タブを選択し、ポートを [ポートの自動割り当て] から 5555 に変更し、仮想パスを、[仮想パス] を /AppServices にしておきましょう。
このサービスをデバッグ実行などで起動しておきましょう。
【クライアント側の作成】
では、クライアント側を作成していきます。
- Visual Studio 2008 で、[Visual C#] - [Windows] - [Windows フォーム アプリケーション] のプロジェクトを新規作成します。
(以降、このプロジェクト名を「TestClient」とします)
- 「System.Web」 の参照(アセンブリの参照)を追加します。
- では、クライアントアプリケーションサービスを使用するように設定をおこないます。
クライアント側では、「資格情報プロバイダ」と呼ばれる資格情報の検証機能を持ったフレームワークを使用することができます。このプロバイダは、IClientFormsAuthenticationCredentialsProvider を実装したクラスで、後ほど作成していきますが今回は MyClientCredentialsProvider という名前のクラスとしましょう。
プロジェクトのプロパティ画面で [サービス] タブを選択し、
- [フォーム認証を使用する] を選択
- [認証サービスの場所] として、以下を設定
http://localhost:5555/AppServices
- [オプション : 資格情報プロバイダ] として、以下を設定
TestClient.MyClientCredentialsProvider, TestClient
(なお、ここで、[Windows 認証を使用する] を選択した場合、クライアント アプリケーション サービスは自動的に SQL Server Compact Edition version 3.5 データベースを使用するように構成されます。)また、[詳細設定] ボタンを押して、キャッシュの設定なども可能です。)
- では、上述した資格情報プロバイダクラス (MyClientCredentialsProvider) を作成します。
ソリューションエクスプローラからプロジェクトを右クリックし、[追加] - [新しい項目] で [クラス] を選択して、MyClientCredentialsProvider.cs としてクラスを新規追加します。
- 追加したクラスのインタフェースとして、以下のように IClientFormsAuthenticationCredentialsProvider を追加します。
using System;
using System.Windows.Forms;
using System.Web.ClientServices.Providers;
namespace TestClient
{
class MyClientCredentialsProvider : IClientFormsAuthenticationCredentialsProvider
{
}
}
- コードエディタ上で「IClientFormsAuthenticationCredentialsProvider」を選択して右クリックし、[インタフェースの実装] を選択します。(GetCredentials というメソッドが追加されます。)
- 上記で追加された GetCredentials メソッドを以下の通り実装します。
ここで使用している LoginForm クラスは、この後実装していきます。
public ClientFormsAuthenticationCredentials GetCredentials()
{
LoginForm loginForm = new LoginForm();
if (loginForm.ShowDialog() == DialogResult.OK)
{
return new ClientFormsAuthenticationCredentials(
loginForm.usernameTextBox.Text, loginForm.passwordTextBox.Text, false);
}
else
{
return null;
}
}
これで、資格情報プロバイダーの実装は完了です。
- では、上記で指定した LoginForm クラスを作成していきましょう。
ソリューションエクスプローラからプロジェクトを右クリックし、[追加] - [新しい項目] で [Windows フォーム] を選択して、LoginForm.cs として新規追加します。
- LoginForm を デザイナーを使って、下図の通りデザインします。
上記のコードにあわせるため、テキストボックスの Name 属性をそれぞれ usernameTextBox, passwordTextBox とし、これらを public 属性にしておきましょう ( ... 雑な方法ですみません)。
また、OK ボタンの DialogResult 属性を「OK」, Cancel ボタンの DialogResult 属性は「Cancel」とします。

- プロジェクト作成時に自動で作成されたフォーム (通常は Form1.cs) のロードイベントを編集して、クライアントアプリケーションサービスのフレームワークを呼び出すように設定します。
Form1 のデザイナー上でダブルクリックをおこない、以下の通りロードイベントの処理を記述します。
private void Form1_Load(object sender, EventArgs e)
{
ValidateUsingCredentialsProvider();
}
private void ValidateUsingCredentialsProvider()
{
bool isAuthorized = false;
System.Web.ClientServices.ConnectivityStatus.IsOffline = false;
try
{
// ValidateUser メソッドで、credentials provider のフレームワークを呼び出す
isAuthorized = System.Web.Security.Membership.ValidateUser(
String.Empty, String.Empty);
}
catch (System.Net.WebException)
{
MessageBox.Show("接続に失敗しました");
Application.Exit();
}
if (!isAuthorized)
{
MessageBox.Show("ログインできません");
Application.Exit();
}
return;
}
上記で、IsOffline を true に設定すると、クライアント側では一度接続した情報をキャッシュして、以降、ローカルの情報を使って接続できるようになります。
クライアントアプリケーションをリビルドし起動すると、最初に上図のログイン画面が表示され、ここに、ID: test, Password: test0 を入力しないとフォームは開かなくなります。
上記ではサンプルを簡単にするためメンバーシッププロバイダーしか使用しませんでしたが、ロールやプロファイルも扱うことができます。ASP.NET のメンバーシップやロール、プロファイルなどのプロバイバーに詳しい方はお分かり頂けると思いますが、上記のようにカスタムなプロバイダーを構築することで Oracle などのストレージに保管されたユーザID/パスワード情報と連携するなど、応用もさまざま可能になります。
また、.NET Framework がデフォルトで提供している SqlMembershipProvider などを使えば、ストレージの作成からプロバイダーの実装まで、既に提供されたものをそのまま使用することができます。Active Directory に保管されたユーザID, パスワード情報などをそのまま使いたいなら、ActiveDirectoryMembershipProvider も使えます。サービス側の web.config を下記のように書き換えてみてください。
<connectionStrings>
<add name="LDAPConnString" connectionString="LDAP://domain_controller_machinename/CN=Users,DC=example,DC=jp" />
</connectionStrings>
<membership defaultProvider="TestMembershipProvider">
<providers>
<!--add name="TestMembershipProvider" type="AppServices.TestMembershipProvider"/-->
<add name="TestMembershipProvider"
type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="LDAPConnString"
connectionUsername="example\Administrator"
connectionPassword="P@ssw0rd"
attributeMapUsername="sAMAccountName"/>
</providers>
</membership>
(connectionUsername と connectionPassword は、Directory の参照権限のあるユーザ (ここでは、ドメインの Administrator を使いました) で接続するように設定しましょう。また、attributeMapUsername は、デフォルトでは userPrincipalName ですので、通常は、ユーザ名として UserName@DomainName の形式を指定しますが、ここでは UserName だけをユーザ名として扱えるように設定しています。)
これで、Active Directory で管理されているユーザログオン名とパスワードがそのまま使用できます。
クライアントアプリケーションサービスについては、MSDN で以下に詳述されています。
http://msdn2.microsoft.com/ja-jp/library/bb384297.aspx