5

単体テストを行っていたところ、WCF で興味深い問題に遭遇しました。を使用するサービスがあり、次のwsHttpBindingように構成されています。

<bindings>
  <wsHttpBinding>
    <binding name="wsHttpUnsecured">
      <security mode="None">
        <transport clientCredentialType="None" />
        <message clientCredentialType="None" />
      </security>
    </binding>
  </wsHttpBinding>

サービスの実装は次のとおりです。

public void DoWork()
{
    OperationContext o = OperationContext.Current;
    if (o == null) return;

    if (o.ServiceSecurityContext.AuthorizationContext.Properties.ContainsKey("x"))
        throw new ApplicationException();

    o.ServiceSecurityContext.AuthorizationContext.Properties.Add("x", "x");
}

ここで行っているのは、操作コンテキストをチェックすることだけです。AuthorizationContext に「X」がない場合は、1 つ追加します。すでに「X」があった場合は、例外アウト。(これは単純なテストとして厳密に設定されています。通常の使用では、これはカスタムの AuthorizationManager と Token Authenticator で発生します)。

基本的に、同じ operationContext と AuthorizationContext 内でメソッドを複数回呼び出すと、例外が発生します。

さて、これが私のテストフィクスチャです。

    [Test]
    public void CallTwice()
    {
        using (var cli1 = new TestBusinessClient())
        {
            cli1.DoWork();
            cli1.Close();
        }
        using (var cli2 = new TestBusinessClient())
        {
            cli2.DoWork();
            cli2.Close();
        }
    }

実行時に何が起こるかを見てみましょう:

  1. が新規TestBusinessClient作成されます。
  2. を呼び出しますDoWork()
  3. DoWork()に「X」が見つかりませんAuthorizationContext.Properties
  4. DoWork()に「X」を追加しAuthorizationContext.Propertiesます。
  5. テスト メソッドは、最初のクライアントを破棄します。
  6. 新しい秒TestBusinessClientが作成されます。
  7. を呼び出しますDoWork()
  8. DoWork() 最後の呼び出しからのプロパティにまだ「X」が見つかります。
  9. DoWork()例外をスローします。

私の質問は次のとおりです。新しいクライアントが同じサービスに接続するときになぜOperationContextandが強制終了されないのですか? AuthorizationContextデフォルトでは呼び出し間にセッション コンテキストが使用されることは理解しwsHttpBindingていますが、そのセッションはクライアントごとになると思います。クライアントの新しいインスタンスに接続すると、WCF セッションとそのコンテキストがすべて更新されることを期待していました。

誰にも考えやアイデアはありますか?ここでの望ましい結果は、2 つの別々のクライアントによるAuthorizationContext.Propertiesへの 2 つの呼び出しの間にリセットされることです。DoWork()


更新 1

PerCallサービスとを設定してみましたPerSessionが、どちらも違いはありませんでした。

また、信頼できるメッセージングのオンとオフを切り替えてサービスを試してみましたが、どちらも何も変わりませんでした。

また、最初の呼び出しと 2 番目の呼び出しで operationContext を保存し、2 つを比較しました。

OperationContext first; // context from first call to DoWork()
OperationContext second; // context from second call to DoWork() 

(first == second) = false
(first.ServiceSecurityContext == second.ServiceSecurityContext) = false
(first.ServiceSecurityContext.AuthorizationContext == second.ServiceSecurityContext.AuthorizationContext) = true

したがって、操作コンテキストが変更/再作成されたように見えますが、AuthorizationContext後続の各サービス呼び出しで何かが同じように設定されています。


更新 2

サーバー側のすべてのものは次のとおりです。

[ServiceContract]
public interface ITestBusiness
{
    [OperationContract(Action = "*", ReplyAction = "*")]
    string DoWork();
}

public class TestBusiness : ITestBusiness
{
    public string DoWork()
    {
        System.ServiceModel.OperationContext o = System.ServiceModel.OperationContext.Current;
        if (o != null)
        {
            if (o.ServiceSecurityContext.AuthorizationContext.Properties.ContainsKey("x"))
                throw new ApplicationException();
            else
                o.ServiceSecurityContext.AuthorizationContext.Properties.Add("x", "x");
        }
    }
    return "";
}

健全性チェックとして、次のことを行いました。

  1. WCF サーバーのインスタンスを開始します (Cassini/統合 VS 2008 サーバーを使用)。
  2. テスト フィクスチャを への 1 回の呼び出しのみに減らしDoWork()ます。
  3. TestDriven.NETVS 2008 内からテストを実行します。
  4. スタンドアロン GUI ツール.dllから同じテストを開き、実行します。NUnit's

最初のテスト実行は成功し、2 回目は失敗します。したがって、これは純粋にサーバー側のようです.2つの異なるプロセスから同じサービス呼び出しを実行すると、最終的に同じAuthorizationContext.

同じユーザー名で同じドメインにログインしているため、WCF の内部の何かがまだ残っていWindowsAuthenticationて、同じコンテキストを再利用しているのではないかと考え始めています。Auth私のサービスはカスタムを使用するように設定されていますAuthorizationManager:

        <serviceBehaviors>
            <behavior name="myBehavior">
                <serviceMetadata httpGetEnabled="false"  />
                <serviceDebug includeExceptionDetailInFaults="true" />
                <serviceAuthorization principalPermissionMode="Custom" serviceAuthorizationManagerType="My.Namespace.CustomAuthMgr" />
            </behavior>

My.Namespace.CustomAuthMgr拡張する場所ServiceAuthorizationManager。にブレークポイントを設定するとCustomAuthMgr.CheckAccess()、最初の呼び出しでoperationContext.ServiceSecurityContext.AuthorizationContextはクリアになり、2 回目の呼び出しでは、前の呼び出しで入力したものがすべて含まれます。これは、WCF によって実行される私自身のコードの最初のメソッドであるため、承認フェーズの前に何かが my AuthorizationContext.


アップデート 3

追加情報: いくつかのことを検証するために、サービスの実装を変更して例外をスローしなくなり、代わりに呼び出し回数のカウンターと現在のスレッド ID を返します。

    public string DoWork()
    {
        var o = System.ServiceModel.OperationContext.Current.ServiceSecurityContext.AuthorizationContext;
        if (o != null)
        {
            if (o.Properties.ContainsKey("x"))
                o.Properties["x"] = (int)o.Properties["x"] + 1;
            else
                o.Properties.Add("x", 1);
        }

        return o.Properties["x"].ToString() + " | " + System.AppDomain.GetCurrentThreadId().ToString();
    }

NUnit次に、 GUIからテストを 1 回実行すると、次のようになります。

1 | 3816

次に、GUI を閉じてNUnit再起動し、テストを再度実行します。

2 | 3816

NUnit次に、 GUI を再び閉じて、TestDriven.NETVisual Studio 内からテストを実行します。

3 | 3816

したがって、クライアントプロセス間で確実に永続化AuthorizationContextされますが、同じスレッドが各サービス呼び出しを処理するため、AuthorizationContext単にスレッド静的か何かですか?

いいえ、スレとは関係ありません。Thread.Sleep(10000);サービスの実装にを追加し、NUnit一度に 2 つの GUI を実行すると、それぞれ「2」が出力されましたが、スレッド ID が異なりました。

2 | 5500
2 | 5764

スレッドAuthorizationContext間でも永続化されています。気の利いた。

4

4 に答える 4

1

サービスの構成、SessionMode に選択する値によって異なります。

[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required)]
public interface ICalculatorSession

http://msdn.microsoft.com/en-us/library/ms733040.aspx

http://msdn.microsoft.com/en-us/library/system.servicemodel.sessionmode.aspx

于 2009-07-27T18:57:39.863 に答える
1

デフォルトでセキュリティがオンになっているため、WsHttp バインディングはデフォルトでセッション モードで実行されます。完全にオフにすると (構成が示すように)、サービス コントラクトで特にセッションが必要でない限り、呼び出しごとのモードに戻ります。

セッションは、セッション タイムアウトが発生しない限り続きます。サーバーで 1 つを定義し、クライアントで別の 1 つを定義すると、短い方が「優先」されます。これを指定する最も簡単な方法は、信頼できるメッセージングを wsHttpBinding に追加することです。

  <wsHttpBinding>
    <binding>
      <reliableSession inactivityTimeout="00:10:00"/>
    </binding>
  </wsHttpBinding>

それ以外の場合は、コードで行うか、カスタム バインディングを定義する必要があります。

または:サービス コントラクトで特定のメソッドを「終了」メソッドとして定義することもできます。これらのメソッドが呼び出されて終了すると、セッションも破棄されます。

[ServiceContract(SessionMode=SessionMode.Required)]
interface YourServiceContract
{
    [OperationContract(IsInitiating = true)]
    void InitMethod();

    [OperationContract]
    void WorkMethod()

    [OperationContract(IsTerminating=true)]
    void EndSessionMethod()
}

ここで、 を呼び出すとEndSessionMethodセッションが終了し、特定のプロキシ インスタンスでサービスを呼び出すことはできなくなります。

または:キャッチされずに FaultException に変換されたサーバー側の未処理の例外も、チャネルに障害を起こし、進行中だった可能性のあるセッションを中止します。

しかし、ご指摘のとおり、私も、クライアント プロキシが破棄されたときにセッション全体と関連するすべてのデータが破棄され、後続の呼び出しのために再作成されることを期待していました。私はあなたの発見に少し驚いています....

マルク

于 2009-07-27T19:09:49.527 に答える
1

それは、セッションとクライアントをどのように定義しているかに大きく依存します。同じプロセスから 2 つの呼び出しを行っている場合、WCF 通信レイヤーがそれらを同じ接続に多重化する可能性があります。

まったく異なるプロセスから 2 つの接続を実際に実行しようとしましたか? 同じ結果が得られますか?

于 2009-07-27T18:39:13.490 に答える