次の方法で共有


トークン認証装置

このサンプルでは、カスタム トークン認証システムを実装する方法を示します。 Windows Communication Foundation (WCF) のトークン認証子は、メッセージで使用されるトークンを検証し、自己一貫性があることを確認し、トークンに関連付けられている ID を認証するために使用されます。

カスタム トークン認証子は、次のようなさまざまな場合に役立ちます。

  • トークンに関連付けられている既定の認証メカニズムをオーバーライドする場合。

  • カスタム トークンを構築する場合。

このサンプルでは、次の例を示します。

  • クライアントがユーザー名とパスワードのペアを使用して認証する方法。

  • カスタム トークン認証システムを使用してサーバーがクライアント資格情報を検証する方法。

  • WCF サービス コードとカスタム トークン認証子の連携方法。

  • サーバーの X.509 証明書を使用してサーバーを認証する方法。

このサンプルでは、カスタム トークン認証プロセスの後に WCF から呼び出し元の ID にアクセスする方法も示します。

サービスは、App.config 構成ファイルを使用して定義された、サービスと通信するための単一のエンドポイントを公開します。 エンドポイントは、アドレス、バインディング、およびコントラクトで構成されます。 バインディングは標準の wsHttpBindingで構成され、セキュリティ モードはメッセージ ( wsHttpBindingの既定のモード) に設定されます。 このサンプルでは、クライアント ユーザー名認証を使用する標準 wsHttpBinding を設定します。 サービスは、 serviceCredentials 動作を使用してサービス証明書も構成します。 securityCredentials動作では、サービス証明書を指定できます。 サービス証明書は、サービスを認証し、メッセージ保護を提供するためにクライアントによって使用されます。 次の構成では、次のセットアップ手順で説明するように、サンプルセットアップ中にインストールされた localhost 証明書を参照します。

<system.serviceModel>
    <services>
      <service
          name="Microsoft.ServiceModel.Samples.CalculatorService"
          behaviorConfiguration="CalculatorServiceBehavior">
        <host>
          <baseAddresses>
            <!-- configure base address provided by host -->
            <add baseAddress ="http://localhost:8000/servicemodelsamples/service" />
          </baseAddresses>
        </host>
        <!-- use base address provided by host -->
        <endpoint address=""
                  binding="wsHttpBinding"
                  bindingConfiguration="Binding1"
                  contract="Microsoft.ServiceModel.Samples.ICalculator" />
      </service>
    </services>

    <bindings>
      <wsHttpBinding>
        <binding name="Binding1">
          <security mode="Message">
            <message clientCredentialType="UserName" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>

    <behaviors>
      <serviceBehaviors>
        <behavior name="CalculatorServiceBehavior">
          <serviceDebug includeExceptionDetailInFaults="False" />
          <!--
          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.
....        -->
          <serviceCredentials>
            <serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>

  </system.serviceModel>

クライアント エンドポイント構成は、構成名、サービス エンドポイントの絶対アドレス、バインディング、コントラクトで構成されます。 クライアント バインドは、適切な ModeclientCredentialTypeで構成されます。

<system.serviceModel>
    <client>
      <endpoint name=""
                address="http://localhost:8000/servicemodelsamples/service"
                binding="wsHttpBinding"
                bindingConfiguration="Binding1"
                contract="Microsoft.ServiceModel.Samples.ICalculator">
      </endpoint>
    </client>

    <bindings>
      <wsHttpBinding>
        <binding name="Binding1">
          <security mode="Message">
            <message clientCredentialType="UserName" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
  </system.serviceModel>

クライアント実装では、使用するユーザー名とパスワードを設定します。

static void Main()
{
     ...
     client.ClientCredentials.UserNamePassword.UserName = username;
     client.ClientCredentials.UserNamePassword.Password = password;
     ...
}

カスタム トークン認証子

カスタム トークン認証システムを作成するには、次の手順に従います。

  1. カスタム トークン認証子を記述します。

    このサンプルでは、ユーザー名に有効な電子メール形式があることを検証するカスタム トークン認証システムを実装しています。 UserNameSecurityTokenAuthenticatorが派生します。 このクラスで最も重要なメソッドは ValidateUserNamePasswordCore(String, String)です。 この方法では、認証子はユーザー名の形式を検証し、ホスト名が不正なドメインからのものではありません。 両方の条件が満たされた場合は、 IAuthorizationPolicy インスタンスの読み取り専用コレクションが返されます。このコレクションは、ユーザー名トークン内に格納されている情報を表すクレームを提供するために使用されます。

    protected override ReadOnlyCollection<IAuthorizationPolicy> ValidateUserNamePasswordCore(string userName, string password)
    {
        if (!ValidateUserNameFormat(userName))
            throw new SecurityTokenValidationException("Incorrect UserName format");
    
        ClaimSet claimSet = new DefaultClaimSet(ClaimSet.System, new Claim(ClaimTypes.Name, userName, Rights.PossessProperty));
        List<IIdentity> identities = new List<IIdentity>(1);
        identities.Add(new GenericIdentity(userName));
        List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>(1);
        policies.Add(new UnconditionalPolicy(ClaimSet.System, claimSet, DateTime.MaxValue.ToUniversalTime(), identities));
        return policies.AsReadOnly();
    }
    
  2. カスタム トークン認証子によって返される承認ポリシーを指定します。

    このサンプルでは、コンストラクターで渡されたクレームと ID のセットを返すIAuthorizationPolicyと呼ばれるUnconditionalPolicyの独自の実装を提供します。

    class UnconditionalPolicy : IAuthorizationPolicy
    {
        String id = Guid.NewGuid().ToString();
        ClaimSet issuer;
        ClaimSet issuance;
        DateTime expirationTime;
        IList<IIdentity> identities;
    
        public UnconditionalPolicy(ClaimSet issuer, ClaimSet issuance, DateTime expirationTime, IList<IIdentity> identities)
        {
            if (issuer == null)
                throw new ArgumentNullException("issuer");
            if (issuance == null)
                throw new ArgumentNullException("issuance");
    
            this.issuer = issuer;
            this.issuance = issuance;
            this.identities = identities;
            this.expirationTime = expirationTime;
        }
    
        public string Id
        {
            get { return this.id; }
        }
    
        public ClaimSet Issuer
        {
            get { return this.issuer; }
        }
    
        public DateTime ExpirationTime
        {
            get { return this.expirationTime; }
        }
    
        public bool Evaluate(EvaluationContext evaluationContext, ref object state)
        {
            evaluationContext.AddToTarget(this, this.issuance);
    
            if (this.identities != null)
            {
                object value;
                IList<IIdentity> contextIdentities;
                if (!evaluationContext.Properties.TryGetValue("Identities", out value))
                {
                    contextIdentities = new List<IIdentity>(this.identities.Count);
                    evaluationContext.Properties.Add("Identities", contextIdentities);
                }
                else
                {
                    contextIdentities = value as IList<IIdentity>;
                }
                foreach (IIdentity identity in this.identities)
                {
                    contextIdentities.Add(identity);
                }
            }
    
            evaluationContext.RecordExpirationTime(this.expirationTime);
            return true;
        }
    }
    
  3. カスタム セキュリティ トークン マネージャーを記述します。

    SecurityTokenManagerは、SecurityTokenAuthenticator メソッドで渡される特定のSecurityTokenRequirement オブジェクトのCreateSecurityTokenAuthenticatorを作成するために使用されます。 セキュリティ トークン マネージャーは、トークン プロバイダーとトークン シリアライザーの作成にも使用されますが、このサンプルでは扱いません。 このサンプルでは、カスタム セキュリティ トークン マネージャーは、 ServiceCredentialsSecurityTokenManager クラスから継承し、渡されたトークン要件がユーザー名認証子が要求されたことを示す場合にカスタム ユーザー名トークン認証子を返すように、 CreateSecurityTokenAuthenticator メソッドをオーバーライドします。

    public class MySecurityTokenManager : ServiceCredentialsSecurityTokenManager
    {
        MyUserNameCredential myUserNameCredential;
    
        public MySecurityTokenManager(MyUserNameCredential myUserNameCredential)
            : base(myUserNameCredential)
        {
            this.myUserNameCredential = myUserNameCredential;
        }
    
        public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
        {
            if (tokenRequirement.TokenType ==  SecurityTokenTypes.UserName)
            {
                outOfBandTokenResolver = null;
                return new MyTokenAuthenticator();
            }
            else
            {
                return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
            }
        }
    }
    
  4. カスタム サービス資格情報を書き込みます。

    サービス資格情報クラスは、サービス用に構成されている資格情報を表すために使用され、トークン認証子、トークン プロバイダー、トークン シリアライザーを取得するために使用されるセキュリティ トークン マネージャーを作成します。

    public class MyUserNameCredential : ServiceCredentials
    {
    
        public MyUserNameCredential()
            : base()
        {
        }
    
        protected override ServiceCredentials CloneCore()
        {
            return new MyUserNameCredential();
        }
    
        public override SecurityTokenManager CreateSecurityTokenManager()
        {
            return new MySecurityTokenManager(this);
        }
    
    }
    
  5. カスタム サービス資格情報を使用するようにサービスを構成します。

    サービスでカスタム サービス資格情報を使用するには、既定のサービス資格情報で既に構成されているサービス証明書をキャプチャした後、既定のサービス資格情報クラスを削除し、構成済みのサービス証明書を使用するように新しいサービス資格情報インスタンスを構成し、この新しいサービス資格情報インスタンスをサービスの動作に追加します。

    ServiceCredentials sc = serviceHost.Credentials;
    X509Certificate2 cert = sc.ServiceCertificate.Certificate;
    MyUserNameCredential serviceCredential = new MyUserNameCredential();
    serviceCredential.ServiceCertificate.Certificate = cert;
    serviceHost.Description.Behaviors.Remove((typeof(ServiceCredentials)));
    serviceHost.Description.Behaviors.Add(serviceCredential);
    

呼び出し元の情報を表示するには、次のコードに示すように PrimaryIdentity を使用します。 Currentには、現在の呼び出し元に関する要求情報が含まれています。

static void DisplayIdentityInformation()
{
    Console.WriteLine("\t\tSecurity context identity  :  {0}",
            ServiceSecurityContext.Current.PrimaryIdentity.Name);
     return;
}

サンプルを実行すると、操作要求と応答がクライアント コンソール ウィンドウに表示されます。 クライアント ウィンドウで 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
    

    セットアップ バッチ ファイルは、Windows SDK コマンド プロンプトから実行するように設計されています。 これは、MSSDK 環境変数が SDK がインストールされているディレクトリを指している必要があります。 この環境変数は、Windows SDK コマンド プロンプト内で自動的に設定されます。

サンプルを設定してビルドするには

  1. Windows Communication Foundation サンプル One-Time セットアップ手順を実行していることを確認します。

  2. ソリューションをビルドするには、「 Windows Communication Foundation サンプルのビルド」の手順に従います。

同じコンピューターでサンプルを実行するには

  1. 管理者特権で開いた Visual Studio コマンド プロンプト内のサンプル インストール フォルダーから Setup.bat を実行します。 これにより、サンプルの実行に必要なすべての証明書がインストールされます。

    Setup.bat バッチ ファイルは、Visual Studio コマンド プロンプトから実行するように設計されています。 Visual Studio コマンド プロンプト内で設定された PATH 環境変数は、Setup.bat スクリプトに必要な実行可能ファイルを含むディレクトリを指します。

  2. service\bin から service.exe を起動します。

  3. \client\bin から client.exe を起動します。 クライアント アクティビティがクライアント コンソール アプリケーションに表示されます。

  4. クライアントとサービスが通信できない場合は、「WCF サンプルのトラブルシューティングのヒント」を参照してください。

複数のコンピューターでサンプルを実行するには

  1. サービス バイナリのサービス コンピューター上にディレクトリを作成します。

  2. サービス プログラム ファイルをサービス コンピューター上のサービス ディレクトリにコピーします。 また、Setup.bat ファイルと Cleanup.bat ファイルをサービス コンピューターにコピーします。

  3. コンピューターの完全修飾ドメイン名を含むサブジェクト名を持つサーバー証明書が必要です。 サービス App.config ファイルは、この新しい証明書名を反映するように更新する必要があります。 サービスを実行するコンピューターの完全修飾ホスト名に %SERVER_NAME% 変数を設定する場合は、Setup.bat を使用して作成できます。 setup.bat ファイルは、管理者特権で開かれた Visual Studio の開発者コマンド プロンプトから実行する必要があることに注意してください。

  4. サーバー証明書をクライアントの CurrentUser-TrustedPeople ストアにコピーします。 サーバー証明書がクライアントの信頼された発行者によって発行される場合を除き、これを行う必要はありません。

  5. サービス コンピューター上の App.config ファイルで、localhost ではなく完全修飾コンピューター名を指定するようにベース アドレスの値を変更します。

  6. サービス コンピューターで、コマンド プロンプトから service.exe を実行します。

  7. クライアント プログラム ファイルを、言語固有のフォルダーの下にある \client\bin\ フォルダーからクライアント コンピューターにコピーします。

  8. クライアント コンピューター上の Client.exe.config ファイルで、サービスの新しいアドレスと一致するようにエンドポイントのアドレス値を変更します。

  9. クライアント コンピューターで、コマンド プロンプトから Client.exe を起動します。

  10. クライアントとサービスが通信できない場合は、「WCF サンプルのトラブルシューティングのヒント」を参照してください。

サンプルの実行後にクリーンアップするには

  1. サンプルの実行が完了したら、samples フォルダーで Cleanup.bat を実行します。