来自不同客户端的调用之间未清除 WCF AuthorizationContext。(是:WCF 服务会话何时真正结束?)

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)

因此,遍历运行时发生的事情:

  1. 一个新的TestBusinessClient被创建。
  2. 它调用DoWork().
  3. DoWork()中没有找到“X” AuthorizationContext.Properties
  4. DoWork()将“X”添加到AuthorizationContext.Properties.
  5. 测试方法配置第一个客户端。
  6. 新的一秒TestBusinessClient被创造出来。
  7. 它调用DoWork().
  8. DoWork() 确实在上次调用的属性找到了“X”。
  9. DoWork() 抛出异常。

所以我的问题是;当新客户端连接到同一服务时,为什么OperationContextAuthorizationContext没有被杀死?我知道wsHttpBinding默认情况下在调用之间使用会话上下文,但我认为该会话将是每个客户端。当我连接到客户端的新实例时,我希望 WCF 会话以及它的上下文全部更新。

任何人有任何想法或想法?此处所需的结果是在两个独立客户端AuthorizationContext.Properties的两次调用之间进行重置DoWork()


更新 1

我尝试设置服务PerCallPerSession,但都没有区别。

我还尝试了打开和关闭可靠消息传递的服务,但都没有改变任何东西。

我还在第一次调用和第二次调用时保存了我的 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)

作为健全性检查,我执行了以下操作:

  1. 启动 WCF 服务器的实例(使用 Cassini / 集成 VS 2008 服务器)。
  2. 将测试装置减少到只有 1 次调用DoWork().
  3. TestDriven.NET在 VS 2008 中运行测试。
  4. .dllNUnit'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)

然后从NUnitGUI运行一次测试结果:

1 | 3816
Run Code Online (Sandbox Code Playgroud)

然后我关闭NUnitGUI,重新启动它,然后再次运行测试:

2 | 3816
Run Code Online (Sandbox Code Playgroud)

然后我再次关闭NUnitGUI,并从TestDriven.NETVisual 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正在跨线程坚持了。漂亮。

Chr*_*her 1

这实际上取决于您如何定义会话和客户端。如果您从同一进程发出两个调用,则 WCF 通信层很可能将它们复用在同一连接上。

您是否真的尝试过从完全不同的进程运行两个连接?你能达到同样的结果吗?