単体テストを行っていたところ、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();
}
}
実行時に何が起こるかを見てみましょう:
- が新規
TestBusinessClient
作成されます。 - を呼び出します
DoWork()
。 DoWork()
に「X」が見つかりませんAuthorizationContext.Properties
。DoWork()
に「X」を追加しAuthorizationContext.Properties
ます。- テスト メソッドは、最初のクライアントを破棄します。
- 新しい秒
TestBusinessClient
が作成されます。 - を呼び出します
DoWork()
。 DoWork()
最後の呼び出しからのプロパティにまだ「X」が見つかります。DoWork()
例外をスローします。
私の質問は次のとおりです。新しいクライアントが同じサービスに接続するときになぜOperationContext
andが強制終了されないのですか? 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 "";
}
健全性チェックとして、次のことを行いました。
- WCF サーバーのインスタンスを開始します (Cassini/統合 VS 2008 サーバーを使用)。
- テスト フィクスチャを への 1 回の呼び出しのみに減らし
DoWork()
ます。 TestDriven.NET
VS 2008 内からテストを実行します。- スタンドアロン 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.NET
Visual Studio 内からテストを実行します。
3 | 3816
したがって、クライアントプロセス間で確実に永続化AuthorizationContext
されますが、同じスレッドが各サービス呼び出しを処理するため、AuthorizationContext
単にスレッド静的か何かですか?
いいえ、スレとは関係ありません。Thread.Sleep(10000);
サービスの実装にを追加し、NUnit
一度に 2 つの GUI を実行すると、それぞれ「2」が出力されましたが、スレッド ID が異なりました。
2 | 5500
2 | 5764
スレッドAuthorizationContext
間でも永続化されています。気の利いた。