Cod*_*ike 5 .net session wcf wcf-sessions
我正在做一些单元测试,并在 WCF 中遇到了一个有趣的问题。我有一个使用wsHttpBinding
, 配置的服务:
<bindings>
<wsHttpBinding>
<binding name="wsHttpUnsecured">
<security mode="None">
<transport clientCredentialType="None" />
<message clientCredentialType="None" />
</security>
</binding>
</wsHttpBinding>
Run Code Online (Sandbox Code Playgroud)
服务的实现是:
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");
}
Run Code Online (Sandbox Code Playgroud)
它在这里所做的只是检查操作上下文,如果 AuthorizationContext 中没有“X”,则添加一个。如果已经有一个“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();
}
}
Run Code Online (Sandbox Code Playgroud)
因此,遍历运行时发生的事情:
TestBusinessClient
被创建。DoWork()
.DoWork()
中没有找到“X” AuthorizationContext.Properties
。DoWork()
将“X”添加到AuthorizationContext.Properties
.TestBusinessClient
被创造出来。DoWork()
.DoWork()
确实在上次调用的属性中找到了“X”。DoWork()
抛出异常。所以我的问题是;当新客户端连接到同一服务时,为什么OperationContext
和AuthorizationContext
没有被杀死?我知道wsHttpBinding
默认情况下在调用之间使用会话上下文,但我认为该会话将是每个客户端。当我连接到客户端的新实例时,我希望 WCF 会话以及它的上下文全部更新。
任何人有任何想法或想法?此处所需的结果是在两个独立客户端AuthorizationContext.Properties
的两次调用之间进行重置DoWork()
。
更新 1
我尝试设置服务PerCall
和PerSession
,但都没有区别。
我还尝试了打开和关闭可靠消息传递的服务,但都没有改变任何东西。
我还在第一次调用和第二次调用时保存了我的 operationContext,并比较了两者:
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
Run Code Online (Sandbox Code Playgroud)
所以看起来操作上下文被更改/重新创建,但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 "";
}
Run Code Online (Sandbox Code Playgroud)
作为健全性检查,我执行了以下操作:
DoWork()
.TestDriven.NET
在 VS 2008 中运行测试。.dll
从NUnit's
独立的 GUI 工具打开相同的测试,并运行它。第一次测试通过,第二次失败。所以看起来这纯粹是服务器端,因为从 2 个不同的进程运行相同的服务调用最终会得到相同的AuthorizationContext
.
我开始怀疑 WCF 内部的某些东西是否仍然停留在WindowsAuthentication
并重用相同的Auth
上下文,因为我使用相同的用户名登录到同一个域?我的服务设置为使用自定义AuthorizationManager
:
<serviceBehaviors>
<behavior name="myBehavior">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceAuthorization principalPermissionMode="Custom" serviceAuthorizationManagerType="My.Namespace.CustomAuthMgr" />
</behavior>
Run Code Online (Sandbox Code Playgroud)
哪里My.Namespace.CustomAuthMgr
延伸ServiceAuthorizationManager
。如果我在 中设置了断点CustomAuthMgr.CheckAccess()
,那么在第一次调用时,它operationContext.ServiceSecurityContext.AuthorizationContext
是清晰的,而在第二次调用中,它包含我从前一次调用中放入的任何内容。这是由 WCF 执行的我自己的代码的第一种方法,因此在授权阶段之前的某些事情正在重新加载我的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();
}
Run Code Online (Sandbox Code Playgroud)
然后从NUnit
GUI运行一次测试结果:
1 | 3816
Run Code Online (Sandbox Code Playgroud)
然后我关闭NUnit
GUI,重新启动它,然后再次运行测试:
2 | 3816
Run Code Online (Sandbox Code Playgroud)
然后我再次关闭NUnit
GUI,并从TestDriven.NET
Visual Studio 中运行测试:
3 | 3816
Run Code Online (Sandbox Code Playgroud)
所以它肯定会AuthorizationContext
在客户端进程之间持久化我,但是同一个线程处理每个服务调用,所以也许AuthorizationContext
只是线程静态或其他什么?
不,这与线程无关。我Thread.Sleep(10000);
在服务实现中添加了一个,然后NUnit
一次运行了 2 个GUI,每个 GUI 都打印出“2”但具有不同的线程 ID:
2 | 5500
2 | 5764
Run Code Online (Sandbox Code Playgroud)
因此,AuthorizationContext
正在跨线程坚持了。漂亮。
这实际上取决于您如何定义会话和客户端。如果您从同一进程发出两个调用,则 WCF 通信层很可能将它们复用在同一连接上。
您是否真的尝试过从完全不同的进程运行两个连接?你能达到同样的结果吗?