简体   繁体   English

在模拟时调用异步WCF服务

[英]Calling an async WCF Service while being impersonated

I have a WCF Service running on a Server, which is configured to accept Kerberos authentication. 我在服务器上运行WCF服务,该服务器配置为接受Kerberos身份验证。

Kerberos works fine and the WCF Service therefore knows, which user is connecting to him. Kerberos工作正常,因此WCF服务知道哪个用户连接到他。 The Service offers everything as Async Methods. 该服务提供一切作为异步方法。 Like this here (just an example for clearity). 就像这里一样(只是一个清晰的例子)。

public ExampleService : IExampleService {
    public Task<string> GetUsernameAsync() {
       return await Task.Run(() => System.Threading.Thread.CurrentPrincipal.Name);
    }
}

On the Client side I have an Controller (it's an MVC-page, but that does not matter), which calls the methods asyncronously. 在客户端,我有一个Controller(它是一个MVC页面,但这没关系),它调用方法异步。

public ExampleController {
    public async Task<ActionResult> Index() {
        using(var serviceClient = ServiceFactory.GetServiceClient())
        using(Security.Impersonation.Impersonate())
        {
            var data = await serviceClient.GetUsernameAsync();
            return View(data);
        }
    }
}

The impersonation works fine, as long as I do not use await. 只要我不使用等待,模仿就可以正常工作。

Since Task<> does not flow the impersonated identity, I'd like to know if there is some possibility, to change the executing user of the Task or to do anything else to make the impersonation work in this use-case. 由于Task<>不会模拟模拟身份,因此我想知道是否有可能更改Task的执行用户或执行任何其他操作以使模拟在此用例中起作用。

I tried a custom awaiter (as it can be done with Culture in that Case), but that does not work at all (Well it just does not impersonate as well). 我试过一个自定义的awaiter(因为它可以在那个案例中用文化完成),但这根本不起作用(好吧它也没有冒充)。

Okay - after some more in depth research I finally found the solution how to flow impersonated windows identities across asynchronous tasks. 好的 - 经过一些更深入的研究后,我终于找到了解决方案如何跨异步任务传递模拟的Windows身份。

The solution is machine-wide and will be set for all (in this case) 64bit ASP.NET 4.5 applications. 该解决方案是机器范围的,将为所有(在本例中)64位ASP.NET 4.5应用程序设置。

Find the aspnet.config file in C:\\Windows\\Microsoft.Net\\Framework64\\v4.0.30319 (probably this will apply for later versions, too) and change the value of legacyImpersonationPolicy to false C:\\Windows\\Microsoft.Net\\Framework64\\v4.0.30319找到aspnet.config文件(可能这也适用于更高版本)并将legacyImpersonationPolicy的值legacyImpersonationPolicy为false

<legacyImpersonationPolicy enabled="false"/>

Make sure to restart IIS (or reboot the machine). 确保重新启动IIS(或重新启动计算机)。
This will then make Impersonation flowing, as long as you use managed methods for the impersonation. 只要您使用托管方法进行模拟,这将使模拟流动。 In my case I impersonate similar to this, which works fine: 在我的情况下,我冒充类似于此,这工作正常:

class Impersonation : IDisposable
    {
        public static Impersonation Impersonate()
        {
            return new Impersonation();
        }

        private WindowsImpersonationContext ImpersonationContext { get; set; }

        private Impersonation()
        {
            var currentIdentity = System.Threading.Thread.CurrentPrincipal.Identity as WindowsIdentity;
            if (currentIdentity != null && currentIdentity.IsAuthenticated)
            {
                ImpersonationContext = currentIdentity.Impersonate();
                return;
            }

            throw new SecurityException("Could not impersonate user identity");
        }

        public void Dispose()
        {
            if(ImpersonationContext != null)
                ImpersonationContext.Dispose();
        }
    }
}

The aspnet.config setting (btw. it did not work to set it in the web.config file) is explained here: http://msdn.microsoft.com/en-us/library/ms229296(v=vs.110).aspx (it basically says, if this is true, we do it the .NET 1.1 way) 这里解释了aspnet.config设置(顺便说一句,它无法在web.config文件中设置它): http//msdn.microsoft.com/en-us/library/ms229296 (v = vs10 ) .aspx (它基本上说,如果这是真的,我们用.NET 1.1的方式)

You can check, if the windows identity is flowed or not by using this method: 您可以使用此方法检查窗口标识是否流动:

System.Security.SecurityContext.IsWindowsIdentityFlowSuppressed()

I disagree with your QUESTION. 我不同意你的问题。

The problem isn't your await . 问题不是你的await But your Task.Run . 但你的Task.Run There should really not be a await Task.Run on you ASP.Net code. 在ASP.Net代码上应该没有await Task.Run The effect of it is an unnecessary thread switch. 它的效果是一个不必要的线程切换。 Since you don't have STA threads on ASP.Net, there is no need for this and it just slows down your code. 由于ASP.Net上没有STA线程,因此不需要这样做,只会减慢代码速度。

If you stick to real threadless Task s you shouldn't have any problems, as you will stay in a single thread. 如果您坚持使用真正无线程的Task那么您应该没有任何问题,因为您将保留在一个线程中。 Unless your application server has a very limited number of clients and a huge amount of CPU bound operations, multi-threading is bad for scaling, as a single user can quickly fill up the schedule of your server. 除非您的应用程序服务器具有非常有限的客户端数量和大量的CPU绑定操作,否则多线程不适合扩展,因为单个用户可以快速填写服务器的计划。

You should really be using Task.FromResult , or TaskCompletionSource.Task to ensure that you remain single-thread. 您应该使用Task.FromResultTaskCompletionSource.Task来确保您保持单线程。 Which co-incidentally will fix your problem with [ThreadLocal] properties. 通过[ThreadLocal]属性可以解决您的问题。

TL:DR TL:DR

Don't use Task.Run on your server-side. 不要在服务器端使用Task.Run Use Task.FromResult so you only have one thread. 使用Task.FromResult因此您只有一个线程。

EDIT:Response 编辑:响应

Which thread? 哪个帖子? On the client side you are still going to use await . 在客户端,您仍将使用await I never said don't use await . 我从来没说过不要await I said DON'T ever use await directly with Task.Run (except on the UI thread). 我说过不要直接使用await Task.Run (UI线程除外)。 I didn't say you should BLOCK a thread. 我没有说你应该BLOCK一个线程。 As your thread should be doing WORK to produce the result that you pass into the Task.FromResult . 因为你的线程应该做WORK来产生你传递给Task.FromResult的结果。 BLOCKING means that you thread does nothing, whilst consuming resources (namely memory). BLOCKING意味着你的线程什么都不做,同时消耗资源(即内存)。 Heck, there isn't even a need to 哎呀,甚至没有必要

Server side should use this pattern: 服务器端应该使用这种模式:

public ExampleService : IExampleService 
{
    public Task<string> GetUsernameAsync() 
    {
       var name = System.Threading.Thread.CurrentPrincipal.Name;
       return Task.FromResult(name);
    }
}

client should remain 客户应该留下来

public ExampleController 
{
    public async Task<ActionResult> Index() 
    {
        using(var serviceClient = ServiceFactory.GetServiceClient())
        using(Security.Impersonation.Impersonate())
        {
            var data = await serviceClient.GetUsernameAsync();
            return View(data);
        }
    }
}

and in the case where your ServiceClient resolves locally, everything runs synchronously (faster and with less resources). 在ServiceClient本地解析的情况下,所有内容都会同步运行(速度更快,资源更少)。 The point here is that you are only applying the Task async pattern for the There is no thread style of async. 这里的要点是你只是为没有异步的线程样式应用Task async模式。 Task.Run is the concurrency style of async, and should only be used when you need to use another thread (either because you are CPU bound, or THIS thread NEEDS to be used for something else). Task.Run是async的并发风格,只应在需要使用另一个线程时使用(因为你是CPU绑定的,或者这个线程需要用于其他东西)。

Since I am in charge of the WCF interfaces here is one solution, which works (but which I do not like, since it is more or less code duplication): 由于我负责WCF接口,因此这是一个有效的解决方案(但我不喜欢,因为它或多或少是代码重复):

[ServiceContract]
interface IExampleService {
    [OperationContract]
    string GetUsername();
}

interface IExampleServiceAsync {
    Task<string> GetUserNameAsync();
}

class ExampleService : IExampleService {
    public string GetUsername() {
        return System.Threading.Thread.CurrentPrincipal.Name;
    }
}

class ExpampleServiceClient : ServiceClient<IExampleService>, IExampleServiceAsync {
    public Task<string> GetUsernameAsync() {
        return Task.Run(() => GetUsername());
    }

    private string GetUsername() {
        using(Security.Impersonation.Impersonate())
        {
            return base.Proxy.GetUsername();
        }
    }
}

I have to say this is a workaround - not a solution - and it changes the Interfaces on Server-Side (to non-Async interfaces only), but at least it is working. 我不得不说这是一种解决方法 - 而不是解决方案 - 它改变了服务器端的接口(仅限非Async接口),但至少它是有效的。

One plus for this solution - you can implement the impersonation as a behavior pattern on top of the ExampleServiceClient. 此解决方案的一个优点 - 您可以将模拟实现为ExampleServiceClient之上的行为模式。

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

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