このサンプルでは、カスタム要求承認ポリシーと、関連付けられているカスタム サービス承認マネージャーを実装する方法を示します。 これは、サービスがサービス操作に対して要求ベースのアクセス チェックを行い、アクセス チェックの前に呼び出し元に特定の権限を付与する場合に便利です。 このサンプルでは、要求を追加するプロセスと、最終的なクレームのセットに対してアクセス チェックを実行するプロセスの両方を示します。 クライアントとサーバーの間のすべてのアプリケーション メッセージは署名され、暗号化されます。 既定では、 wsHttpBinding
バインドでは、有効な Windows アカウントへのログオンにクライアントから提供されたユーザー名とパスワードが使用されます。 このサンプルでは、カスタム UserNamePasswordValidator を使用してクライアントを認証する方法を示します。 さらに、このサンプルでは、X.509 証明書を使用してサービスに対して認証するクライアントを示します。 このサンプルでは、 IAuthorizationPolicy と ServiceAuthorizationManagerの実装を示します。これらの間で、特定のユーザーに対してサービスの特定のメソッドへのアクセスを許可します。 このサンプルは メッセージ セキュリティ ユーザー名に基づいていますが、 ServiceAuthorizationManager が呼び出される前に要求変換を実行する方法を示します。
注
このサンプルのセットアップ手順とビルド手順は、このトピックの最後にあります。
まとめると、このサンプルは次の方法を示しています。
クライアントは、ユーザー名とパスワードを使用して認証できます。
クライアントは、X.509 証明書を使用して認証できます。
サーバーは、カスタム
UsernamePassword
検証コントロールに対してクライアント資格情報を検証します。サーバーは、サーバーの X.509 証明書を使用して認証されます。
サーバーは、 ServiceAuthorizationManager を使用して、サービス内の特定のメソッドへのアクセスを制御できます。
IAuthorizationPolicyを実装する方法。
サービスは、構成ファイル App.configを使用して定義された、サービスと通信するための 2 つのエンドポイントを公開します。各エンドポイントは、アドレス、バインディング、およびコントラクトで構成されます。 1 つのバインディングは、WS-Security とクライアント ユーザー名認証を使用する標準の wsHttpBinding
バインドで構成されます。 もう 1 つのバインドは、WS-Security およびクライアント証明書認証を使用する標準の wsHttpBinding
バインドで構成されます。
<behavior>は、サービス認証にユーザー資格情報を使用することを指定します。 サーバー証明書には、SubjectName
プロパティの <serviceCertificate> のfindValue
属性と同じ値が含まれている必要があります。
<system.serviceModel>
<services>
<service name="Microsoft.ServiceModel.Samples.CalculatorService"
behaviorConfiguration="CalculatorServiceBehavior">
<host>
<baseAddresses>
<!-- configure base address provided by host -->
<add baseAddress ="http://localhost:8001/servicemodelsamples/service"/>
</baseAddresses>
</host>
<!-- use base address provided by host, provide two endpoints -->
<endpoint address="username"
binding="wsHttpBinding"
bindingConfiguration="Binding1"
contract="Microsoft.ServiceModel.Samples.ICalculator" />
<endpoint address="certificate"
binding="wsHttpBinding"
bindingConfiguration="Binding2"
contract="Microsoft.ServiceModel.Samples.ICalculator" />
</service>
</services>
<bindings>
<wsHttpBinding>
<!-- Username binding -->
<binding name="Binding1">
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</binding>
<!-- X509 certificate binding -->
<binding name="Binding2">
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="CalculatorServiceBehavior" >
<serviceDebug includeExceptionDetailInFaults ="true" />
<serviceCredentials>
<!--
The serviceCredentials behavior allows one to specify a custom validator for username/password combinations.
-->
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Microsoft.ServiceModel.Samples.MyCustomUserNameValidator, service" />
<!--
The serviceCredentials behavior allows one to specify authentication constraints on client certificates.
-->
<clientCertificate>
<!--
Setting the certificateValidationMode to PeerOrChainTrust means that if the certificate
is in the user's Trusted People store, then it will be trusted without performing a
validation of the certificate's issuer chain. This setting is used here for convenience so that the
sample can be run without having to have certificates issued by a certification authority (CA).
This setting is less secure than the default, ChainTrust. The security implications of this
setting should be carefully considered before using PeerOrChainTrust in production code.
-->
<authentication certificateValidationMode="PeerOrChainTrust" />
</clientCertificate>
<!--
The serviceCredentials behavior allows one to define a service certificate.
A service certificate is used by a client to authenticate the service and provide message protection.
This configuration references the "localhost" certificate installed during the setup instructions.
-->
<serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
</serviceCredentials>
<serviceAuthorization serviceAuthorizationManagerType="Microsoft.ServiceModel.Samples.MyServiceAuthorizationManager, service">
<!--
The serviceAuthorization behavior allows one to specify custom authorization policies.
-->
<authorizationPolicies>
<add policyType="Microsoft.ServiceModel.Samples.CustomAuthorizationPolicy.MyAuthorizationPolicy, PolicyLibrary" />
</authorizationPolicies>
</serviceAuthorization>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
各クライアント エンドポイント構成は、構成名、サービス エンドポイントの絶対アドレス、バインディング、コントラクトで構成されます。 クライアント バインディングは、この場合、<security で指定された適切なセキュリティ モードで構成され><メッセージで指定されたclientCredentialType
>。
<system.serviceModel>
<client>
<!-- Username based endpoint -->
<endpoint name="Username"
address="http://localhost:8001/servicemodelsamples/service/username"
binding="wsHttpBinding"
bindingConfiguration="Binding1"
behaviorConfiguration="ClientCertificateBehavior"
contract="Microsoft.ServiceModel.Samples.ICalculator" >
</endpoint>
<!-- X509 certificate based endpoint -->
<endpoint name="Certificate"
address="http://localhost:8001/servicemodelsamples/service/certificate"
binding="wsHttpBinding"
bindingConfiguration="Binding2"
behaviorConfiguration="ClientCertificateBehavior"
contract="Microsoft.ServiceModel.Samples.ICalculator">
</endpoint>
</client>
<bindings>
<wsHttpBinding>
<!-- Username binding -->
<binding name="Binding1">
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</binding>
<!-- X509 certificate binding -->
<binding name="Binding2">
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<behavior name="ClientCertificateBehavior">
<clientCredentials>
<serviceCertificate>
<!--
Setting the certificateValidationMode to PeerOrChainTrust
means that if the certificate
is in the user's Trusted People store, then it will be
trusted without performing a
validation of the certificate's issuer chain. This setting
is used here for convenience so that the
sample can be run without having to have certificates
issued by a certification authority (CA).
This setting is less secure than the default, ChainTrust.
The security implications of this
setting should be carefully considered before using
PeerOrChainTrust in production code.
-->
<authentication certificateValidationMode = "PeerOrChainTrust" />
</serviceCertificate>
</clientCredentials>
</behavior>
</behaviors>
</system.serviceModel>
ユーザー名ベースのエンドポイントの場合、クライアント実装は、使用するユーザー名とパスワードを設定します。
// Create a client with Username endpoint configuration
CalculatorClient client1 = new CalculatorClient("Username");
client1.ClientCredentials.UserName.UserName = "test1";
client1.ClientCredentials.UserName.Password = "1tset";
try
{
// Call the Add service operation.
double value1 = 100.00D;
double value2 = 15.99D;
double result = client1.Add(value1, value2);
Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
...
}
catch (Exception e)
{
Console.WriteLine("Call failed : {0}", e.Message);
}
client1.Close();
証明書ベースのエンドポイントの場合、クライアント実装は使用するクライアント証明書を設定します。
// Create a client with Certificate endpoint configuration
CalculatorClient client2 = new CalculatorClient("Certificate");
client2.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "test1");
try
{
// Call the Add service operation.
double value1 = 100.00D;
double value2 = 15.99D;
double result = client2.Add(value1, value2);
Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
...
}
catch (Exception e)
{
Console.WriteLine("Call failed : {0}", e.Message);
}
client2.Close();
このサンプルでは、カスタム UserNamePasswordValidator を使用して、ユーザー名とパスワードを検証します。 このサンプルでは、UserNamePasswordValidatorから派生したMyCustomUserNamePasswordValidator
を実装しています。 詳細については、 UserNamePasswordValidator に関するドキュメントを参照してください。 このカスタム 検証コントロール サンプルでは、 UserNamePasswordValidatorとの統合を示すために、次のコードに示すように、ユーザー名とパスワードのペアを受け入れるための Validate メソッドを実装しています。
public class MyCustomUserNamePasswordValidator : UserNamePasswordValidator
{
// This method validates users. It allows in two users,
// test1 and test2 with passwords 1tset and 2tset respectively.
// This code is for illustration purposes only and
// MUST NOT be used in a production environment because it
// is NOT secure.
public override void Validate(string userName, string password)
{
if (null == userName || null == password)
{
throw new ArgumentNullException();
}
if (!(userName == "test1" && password == "1tset") && !(userName == "test2" && password == "2tset"))
{
throw new SecurityTokenException("Unknown Username or Password");
}
}
}
サービス コードで検証コントロールを実装したら、使用する検証コントロール インスタンスについてサービス ホストに通知する必要があります。 これを行うには、次のコードを使用します。
Servicehost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
serviceHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new MyCustomUserNamePasswordValidatorProvider();
または、構成で同じことを行うことができます。
<behavior>
<serviceCredentials>
<!--
The serviceCredentials behavior allows one to specify a custom validator for username/password combinations.
-->
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Microsoft.ServiceModel.Samples.MyCustomUserNameValidator, service" />
...
</serviceCredentials>
</behavior>
Windows Communication Foundation (WCF) には、アクセス チェックを実行するためのクレーム ベースの豊富なモデルが用意されています。 ServiceAuthorizationManager オブジェクトは、アクセス チェックを実行し、クライアントに関連付けられている要求がサービス メソッドへのアクセスに必要な要件を満たしているかどうかを判断するために使用されます。
このサンプルでは、デモンストレーションの目的で、CheckAccessCore メソッドを実装するServiceAuthorizationManagerの実装を示します。これにより、呼び出しが許可される操作のアクション URI が値http://example.com/claims/allowedoperation
型の要求に基づいて、ユーザーがメソッドにアクセスできるようになります。
public class MyServiceAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
string action = operationContext.RequestContext.RequestMessage.Headers.Action;
Console.WriteLine("action: {0}", action);
foreach(ClaimSet cs in operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets)
{
if ( cs.Issuer == ClaimSet.System )
{
foreach (Claim c in cs.FindClaims("http://example.com/claims/allowedoperation", Rights.PossessProperty))
{
Console.WriteLine("resource: {0}", c.Resource.ToString());
if (action == c.Resource.ToString())
return true;
}
}
}
return false;
}
}
カスタム ServiceAuthorizationManager を実装したら、使用する ServiceAuthorizationManager についてサービス ホストに通知する必要があります。 これは、次のコードに示すように行われます。
<behavior>
...
<serviceAuthorization serviceAuthorizationManagerType="Microsoft.ServiceModel.Samples.MyServiceAuthorizationManager, service">
...
</serviceAuthorization>
</behavior>
実装する主な IAuthorizationPolicy メソッドは、 Evaluate(EvaluationContext, Object) メソッドです。
public class MyAuthorizationPolicy : IAuthorizationPolicy
{
string id;
public MyAuthorizationPolicy()
{
id = Guid.NewGuid().ToString();
}
public bool Evaluate(EvaluationContext evaluationContext,
ref object state)
{
bool bRet = false;
CustomAuthState customstate = null;
if (state == null)
{
customstate = new CustomAuthState();
state = customstate;
}
else
customstate = (CustomAuthState)state;
Console.WriteLine("In Evaluate");
if (!customstate.ClaimsAdded)
{
IList<Claim> claims = new List<Claim>();
foreach (ClaimSet cs in evaluationContext.ClaimSets)
foreach (Claim c in cs.FindClaims(ClaimTypes.Name,
Rights.PossessProperty))
foreach (string s in
GetAllowedOpList(c.Resource.ToString()))
{
claims.Add(new
Claim("http://example.com/claims/allowedoperation",
s, Rights.PossessProperty));
Console.WriteLine("Claim added {0}", s);
}
evaluationContext.AddClaimSet(this,
new DefaultClaimSet(this.Issuer,claims));
customstate.ClaimsAdded = true;
bRet = true;
}
else
{
bRet = true;
}
return bRet;
}
...
}
前のコードは、 Evaluate(EvaluationContext, Object) メソッドが、処理に影響を与える新しい要求が追加されていないことを確認し、特定の要求を追加する方法を示しています。 許可される要求は、 GetAllowedOpList
メソッドから取得されます。これは、ユーザーが実行できる特定の操作の一覧を返すために実装されます。 承認ポリシーは、特定の操作にアクセスするための要求を追加します。 これは、後でアクセス チェックの決定を実行するために ServiceAuthorizationManager によって使用されます。
カスタム IAuthorizationPolicy を実装したら、使用する承認ポリシーについてサービス ホストに通知する必要があります。
<serviceAuthorization>
<authorizationPolicies>
<add policyType='Microsoft.ServiceModel.Samples.CustomAuthorizationPolicy.MyAuthorizationPolicy, PolicyLibrary' />
</authorizationPolicies>
</serviceAuthorization>
サンプルを実行すると、操作要求と応答がクライアント コンソール ウィンドウに表示されます。 クライアントは、Add メソッド、Subtract メソッド、および Multiple メソッドを正常に呼び出し、Divide メソッドを呼び出そうとしたときに "Access is denied" メッセージを取得します。 クライアント ウィンドウで Enter キーを押して、クライアントをシャットダウンします。
Batch ファイルのセットアップ
このサンプルに用意されている Setup.bat バッチ ファイルを使用すると、適切な証明書を使用してサーバーを構成し、サーバー証明書ベースのセキュリティを必要とする自己ホスト型アプリケーションを実行できるようになります。
次に、適切な構成で実行するように変更できるように、バッチ ファイルのさまざまなセクションの概要を示します。
サーバー証明書の作成。
Setup.bat バッチ ファイルの次の行は、使用するサーバー証明書を作成します。 %SERVER_NAME% 変数は、サーバー名を指定します。 この変数を変更して、独自のサーバー名を指定します。 既定値は localhost です。
echo ************ echo Server cert setup starting echo %SERVER_NAME% echo ************ echo making server cert echo ************ makecert.exe -sr LocalMachine -ss MY -a sha1 -n CN=%SERVER_NAME% -sky exchange -pe
サーバー証明書をクライアントの信頼された証明書ストアにインストールする。
Setup.bat バッチ ファイル内の次の行は、クライアントの信頼できるユーザー ストアにサーバー証明書をコピーします。 この手順は、Makecert.exe によって生成された証明書がクライアント システムによって暗黙的に信頼されないために必要です。 クライアントの信頼されたルート証明書 (Microsoft が発行した証明書など) にルート化された証明書が既にある場合、クライアント証明書ストアにサーバー証明書を設定するこの手順は必要ありません。
certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
クライアント証明書の作成。
Setup.bat バッチ ファイルの次の行は、使用するクライアント証明書を作成します。 %USER_NAME% 変数は、サーバー名を指定します。 これは
IAuthorizationPolicy
が検索する名前であるため、この値は "test1" に設定されます。 %USER_NAME% の値を変更する場合は、IAuthorizationPolicy.Evaluate
メソッドの対応する値を変更する必要があります。証明書は、CurrentUser ストアの場所の下の個人用 (個人用) ストアに格納されます。
echo ************ echo making client cert echo ************ makecert.exe -sr CurrentUser -ss MY -a sha1 -n CN=%CLIENT_NAME% -sky exchange -pe
クライアント証明書をサーバーの信頼された証明書ストアにインストールする。
Setup.bat バッチ ファイル内の次の行は、クライアント証明書を信頼されたユーザー ストアにコピーします。 この手順は、Makecert.exe によって生成された証明書がサーバー システムによって暗黙的に信頼されないために必要です。 信頼されたルート証明書 (Microsoft が発行した証明書など) にルート化された証明書が既にある場合、サーバー証明書ストアにクライアント証明書を設定するこの手順は必要ありません。
certmgr.exe -add -r CurrentUser -s My -c -n %CLIENT_NAME% -r LocalMachine -s TrustedPeople
サンプルを設定してビルドするには
ソリューションをビルドするには、「 Windows Communication Foundation サンプルのビルド」の手順に従います。
サンプルを単一コンピューター構成で実行するか、複数コンピューター構成で実行するかに応じて、次の手順に従います。
注
Svcutil.exe を使用してこのサンプルの構成を再生成する場合は、クライアント構成のエンドポイント名をクライアント コードと一致するように変更してください。
同じコンピューターでサンプルを実行するには
管理者特権で Visual Studio の開発者コマンド プロンプトを開き、サンプルのインストール フォルダーから Setup.bat 実行します。 これにより、サンプルの実行に必要なすべての証明書がインストールされます。
注
Setup.bat バッチ ファイルは、Visual Studio の開発者コマンド プロンプトから実行するように設計されています。 Visual Studio の開発者コマンド プロンプト内で設定された PATH 環境変数は、 Setup.bat スクリプトで必要な実行可能ファイルが含まれているディレクトリを指します。
service\bin から Service.exe を起動します。
\client\bin から Client.exe を起動します。 クライアント アクティビティがクライアント コンソール アプリケーションに表示されます。
クライアントとサービスが通信できない場合は、「WCF サンプルのトラブルシューティングのヒント」を参照してください。
複数のコンピューターでサンプルを実行するには
サービス コンピューター上にディレクトリを作成します。
サービス プログラム ファイルを \service\bin からサービス コンピューター上のディレクトリにコピーします。 また、Setup.bat、Cleanup.bat、GetComputerName.vbs、ImportClientCert.bat ファイルをサービス コンピューターにコピーします。
クライアント コンピューター上にクライアント バイナリ用のディレクトリを作成します。
クライアント プログラム ファイルをクライアント コンピューター上のクライアント ディレクトリにコピーします。 また、Setup.bat、Cleanup.bat、および ImportServiceCert.bat ファイルをクライアントにコピーします。
サーバーで、管理者特権で開いた Visual Studio の開発者コマンド プロンプトで
setup.bat service
を実行します。service
引数を指定してsetup.bat
を実行すると、コンピューターの完全修飾ドメイン名を持つサービス証明書が作成され、サービス証明書が Service.cer という名前のファイルにエクスポートされます。コンピューターの完全修飾ドメイン名と同じ新しい証明書名 (<serviceCertificate の
findValue
属性>) を反映するようにService.exe.configを編集します。 また、<service>/<baseAddresses> 要素のコンピューター名を localhost からサービス コンピューターの完全修飾名に変更します。Service.cer ファイルをサービス ディレクトリからクライアント コンピューター上のクライアント ディレクトリにコピーします。
クライアントで、管理者特権で開いた Visual Studio の開発者コマンド プロンプトで
setup.bat client
を実行します。client
引数を指定してsetup.bat
を実行すると、test1 という名前のクライアント証明書が作成され、クライアント証明書が Client.cer という名前のファイルにエクスポートされます。クライアント コンピューター上の Client.exe.config ファイルで、サービスの新しいアドレスと一致するようにエンドポイントのアドレス値を変更します。 これを行うには、 localhost をサーバーの完全修飾ドメイン名に置き換えます。
Client.cer ファイルをクライアント ディレクトリからサーバー上のサービス ディレクトリにコピーします。
クライアントで、管理者特権で開いた Visual Studio の開発者コマンド プロンプトで ImportServiceCert.bat を実行します。
これにより、Service.cer ファイルから CurrentUser - TrustedPeople ストアにサービス証明書がインポートされます。
サーバーで、管理者特権で開いた Visual Studio の開発者コマンド プロンプトで ImportClientCert.bat を実行します。
これにより、Client.cer ファイルから LocalMachine - TrustedPeople ストアにクライアント証明書がインポートされます。
サーバー コンピューターで、コマンド プロンプト ウィンドウから Service.exe を起動します。
クライアント コンピューターで、コマンド プロンプト ウィンドウから Client.exe を起動します。
クライアントとサービスが通信できない場合は、「WCF サンプルのトラブルシューティングのヒント」を参照してください。
サンプルの後にクリーンアップする
サンプルの後でクリーンアップするには、サンプルの実行が完了したら、samples フォルダーで Cleanup.bat を実行します。 これにより、証明書ストアからサーバー証明書とクライアント証明書が削除されます。
注
このスクリプトは、コンピューター間でこのサンプルを実行するときに、クライアント上のサービス証明書を削除しません。 コンピューター間で証明書を使用する WCF サンプルを実行している場合は、CurrentUser - TrustedPeople ストアにインストールされているサービス証明書を必ずクリアしてください。 これを行うには、次のコマンドを使用します。 certmgr -del -r CurrentUser -s TrustedPeople -c -n <Fully Qualified Server Machine Name>
例: certmgr -del -r CurrentUser -s TrustedPeople -c -n server1.contoso.com
.