简体   繁体   English

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

[英]WCF AuthorizationContext not cleared between calls from different clients. (was: When does a WCF service session actually end?)

I was doing some unit testing, and ran into an interesting problem in WCF. 我正在进行一些单元测试,并在WCF中遇到了一个有趣的问题。 I have a service using the wsHttpBinding , configured as such: 我有一个使用wsHttpBinding的服务,配置如下:

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

The implementation of the service is: 该服务的实施是:

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");
}

All it is doing here is checking the operation context, and if there is not an "X" in the AuthorizationContext, then add one. 这里所做的只是检查操作上下文,如果AuthorizationContext中没有“X”,则添加一个。 If there was already an "X", then exception out. 如果已经有“X”,那么异常就出来了。 (this is set up strictly as a simple test. in normal use, this would be happening in a custom AuthorizationManager and Token Authenticator). (这被严格地设置为一个简单的测试。在正常使用中,这将发生在自定义AuthorizationManager和Token Authenticator中)。

So basically, if we call the method more than once within the same operationContext and AuthorizationContext, then we will see an exception. 所以基本上,如果我们在同一个operationContext和AuthorizationContext中多次调用该方法,那么我们将看到一个异常。

Now, here is my test fixture. 现在,这是我的测试夹具。

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

So walking through what happens at runtime: 因此,了解运行时发生的事情:

  1. A new TestBusinessClient is created. 创建一个新的TestBusinessClient
  2. It makes a call to DoWork() . 它调用DoWork()
  3. DoWork() does not find "X" in the AuthorizationContext.Properties . DoWork()AuthorizationContext.Properties找不到“X”。
  4. DoWork() adds "X" to the AuthorizationContext.Properties . DoWork()将“X”添加到AuthorizationContext.Properties
  5. The test method disposes of the first client. 测试方法处理第一个客户端。
  6. A new second TestBusinessClient is created. 创建了新的第二个TestBusinessClient
  7. It makes a call to DoWork() . 它调用DoWork()
  8. DoWork() does find "X" still in the properties from the last call. DoWork() 确实在最后一次调用的属性找到了“X”。
  9. DoWork() throws an exception. DoWork()抛出异常。

So my question is; 所以我的问题是; why is the OperationContext and AuthorizationContext not killed off when a new client connects to the same service? 为什么当新客户端连接到同一服务时, OperationContextAuthorizationContext没有被终止? I do understand that wsHttpBinding by default uses a session context between the calls, but I would think that session would be per client. 我确实理解wsHttpBinding默认情况下使用调用之间的会话上下文,但我认为会话将​​是每个客户端。 I expected the WCF session, and therefore its contexts, to all renew when I connect with a new instance of a client. 当我连接到客户端的新实例时,我期望WCF会话及其上下文全部更新。

Anyone have any thoughts or ideas? 任何人有任何想法或想法? The desired result here is for AuthorizationContext.Properties to be reset between the two calls to DoWork() by the two separate clients. 这里所需的结果是AuthorizationContext.Properties在两个独立客户端对DoWork()的两次调用之间重置。


Update 1 更新1

I tried setting the service PerCall and PerSession , and neither made a difference. 我尝试设置服务PerCallPerSession ,也不做了一个差异。

I also tried the service with reliable messaging on and off, and neither changed anything. 我也尝试了可靠的消息传递开启和关闭服务,并没有改变任何东西。

I also saved off my operationContext at the first call and the second call, and compared the two: 我还在第一次调用和第二次调用时保存了我的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

So it kind of looks like the operation context is changed / recreated, but something is setting the same AuthorizationContext on each subsequent service call. 所以它看起来像操作上下文被更改/重新创建,但有些事情是在每个后​​续服务调用上设置相同的AuthorizationContext


Update 2 更新2

Here is all the server-side stuff: 这是所有服务器端的东西:

[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 "";
}

As a sanity check, I did the following: 作为一个完整性检查,我做了以下事情:

  1. Start an instance of the WCF server (using Cassini / integrated VS 2008 server). 启动WCF服务器的实例(使用Cassini /集成的VS 2008服务器)。
  2. Reduce the test fixture to only 1 call to DoWork() . 将测试夹具减少到只有1次调用DoWork()
  3. Run the test from TestDriven.NET within VS 2008. 在VS 2008中运行TestDriven.NET的测试。
  4. Open the same test .dll from NUnit's standalone GUI tool, and run it. NUnit's独立GUI工具中打开相同的测试.dll ,然后运行它。

The first test run passes, and the second fails. 第一次测试运行,第二次测试失败。 So it seems this is purely server side, as running the same service call from 2 different processes ends up with the same AuthorizationContext . 所以看起来这纯粹是服务器端,因为从2个不同进程运行相同的服务调用最终会使用相同的AuthorizationContext

I'm starting to wonder if maybe something internal to WCF is still stuck on WindowsAuthentication and reusing the same Auth Context since I'm logged into the same domain with the same user name? 我开始怀疑WCF的内部是否仍然停留在WindowsAuthentication并重用相同的Auth Context,因为我使用相同的用户名登录到同一个域? My service is set to use a custom AuthorizationManager : 我的服务设置为使用自定义AuthorizationManager

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

Where My.Namespace.CustomAuthMgr extends ServiceAuthorizationManager . My.Namespace.CustomAuthMgr扩展ServiceAuthorizationManager If I set a breakpoint in CustomAuthMgr.CheckAccess() , then on the first call, operationContext.ServiceSecurityContext.AuthorizationContext is clear, and on the second call, it contains whatever I put in it from the previous call. 如果我在CustomAuthMgr.CheckAccess()设置断点,那么在第一次调用时, operationContext.ServiceSecurityContext.AuthorizationContext是清除的,在第二次调用时,它包含我在上一次调用中放入的任何内容。 This is the first method of my own code that is executed by WCF, so something before the Authorization phase is reloading my AuthorizationContext . 这是我自己的代码由WCF执行的第一种方法,因此在授权阶段之前的一些方法是重新加载我的AuthorizationContext


Update 3 更新3

Some added info: In order to validate some things, I changes my service implementation to no longer throw an exception, and instead return a counter of the number of times called, plus the current thread ID: 一些添加的信息:为了验证一些东西,我改变我的服务实现不再抛出异常,而是返回一个被调用次数的计数器,加上当前的线程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();
    }

Then running the test once from NUnit GUI results in: 然后从NUnit GUI运行测试一次导致:

1 | 3816

I then close the NUnit GUI, restart it, and run the test again: 然后我关闭NUnit GUI,重新启动它,然后再次运行测试:

2 | 3816

I then close the NUnit GUI again, and run the test from TestDriven.NET within Visual Studio: 然后我再次关闭NUnit GUI,并在Visual Studio中从TestDriven.NET运行测试:

3 | 3816

So its definitely persisting my AuthorizationContext between client processes, but the same thread handles each service call, so maybe AuthorizationContext is just Thread-static or something? 所以它肯定会在客户端进程之间保持我的AuthorizationContext ,但是同一个线程处理每个服务调用,所以AuthorizationContext可能只是Thread-static还是什么?

Nope, it has nothing to do with the thread. 不,它与线程无关。 I added a Thread.Sleep(10000); 我添加了一个Thread.Sleep(10000); to the service implementation, then ran 2 NUnit GUIs at once, and each one printed out "2" but with different thread IDs: 到服务实现,然后一次运行2个NUnit GUI,每个GUI打印出“2”但具有不同的线程ID:

2 | 5500
2 | 5764

So AuthorizationContext is being persisted across threads too. 因此AuthorizationContext也在线程之间保持不变。 Nifty. 俏皮。

That really depends on how you are defining session and client. 这实际上取决于您如何定义会话和客户端。 If you are making two calls from the same process, there is every possibility that the WCF communication layer is multiplexing them down the same connection. 如果您从同一进程进行两次调用,则WCF通信层有可能在同一连接中多路复用它们。

Have you actually tried to run two connects from entirely different processes? 你真的试过从完全不同的进程运行两个连接吗? Do you achieve the same results? 你取得了相同的结果吗?

It depends on the configuration of your service, what value you choose for your SessionMode: 这取决于您的服务配置,您为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/ms733040.aspx

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

WsHttp binding will by default run in session mode, since security is turned on by default. 默认情况下,WsHttp绑定将在会话模式下运行,因为安全性已打开。 If you turn it off completely (as your config seems to show), it'll revert to per-call mode, unless your service contract specifically requires a session. 如果您完全关闭它(如配置似乎显示),它将恢复为每个呼叫模式,除非您的服务合同特别需要会话。

The session will last as long as none of the session timeouts have occured - you can define one on the server, and another one on the client, and the shorter one "wins". 只要没有会话超时发生,会话就会持续 - 您可以在服务器上定义一个,在客户端定义另一个,并且较短的一个“获胜”。 The easiest way to specify it is by adding the reliable messaging to the wsHttpBinding: 指定它的最简单方法是向wsHttpBinding添加可靠的消息:

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

Otherwise you'll have to do it in code or by defining your custom binding. 否则,您必须在代码中或通过定义自定义绑定来执行此操作。

OR: you can also define certain methods on your service contract to be "terminating" methods, and if those have been called and terminate, the session will be destroyed as well. 或者:您还可以将服务合同上的某些方法定义为“终止”方法,如果已经调用并终止这些方法,则会话也将被销毁。

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

    [OperationContract]
    void WorkMethod()

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

Here, calling the EndSessionMethod will terminate the session and no more calls can be made to the service on the particular proxy instance. 在这里,调用EndSessionMethod将终止会话,并且不再对特定代理实例上的服务进行调用。

OR: an unhandled exception on the server side which is not caught and turned into a FaultException will also fault the channel and abort any session that might have been ongoing. 或者:服务器端的未处理异常(未捕获并转换为FaultException)也会使通道出错并中止可能正在进行的任何会话。

But as you point out - I, too, would have expected the entire session and all associated data to be discarded when the client proxy is disposed of, and recreated for a subsequent call. 但正如您所指出的那样 - 我也期望在客户端代理被丢弃时丢弃整个会话和所有相关数据,并为后续调用重新创建。 I'm a bit surprised by your findings.... 你对你的发现有点惊讶......

Marc

I have similar issue - I see that AuthorizationContext stays the same after each call. 我有类似的问题 - 我看到每次调用后AuthorizationContext保持不变。

I've noticed that in your config file you have 我注意到你的配置文件中有

<serviceAuthorization principalPermissionMode="Custom" ....

Try to remove principalPermissionMode="Custom" . 尝试删除principalPermissionMode =“Custom” At least in my case every new call comes with null AuthorizationContext. 至少在我的情况下,每个新调用都带有null AuthorizationContext。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM