繁体   English   中英

在模拟时调用异步WCF服务

[英]Calling an async WCF Service while being impersonated

我在服务器上运行WCF服务,该服务器配置为接受Kerberos身份验证。

Kerberos工作正常,因此WCF服务知道哪个用户连接到他。 该服务提供一切作为异步方法。 就像这里一样(只是一个清晰的例子)。

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

在客户端,我有一个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);
        }
    }
}

只要我不使用等待,模仿就可以正常工作。

由于Task<>不会模拟模拟身份,因此我想知道是否有可能更改Task的执行用户或执行任何其他操作以使模拟在此用例中起作用。

我试过一个自定义的awaiter(因为它可以在那个案例中用文化完成),但这根本不起作用(好吧它也没有冒充)。

好的 - 经过一些更深入的研究后,我终于找到了解决方案如何跨异步任务传递模拟的Windows身份。

该解决方案是机器范围的,将为所有(在本例中)64位ASP.NET 4.5应用程序设置。

C:\\Windows\\Microsoft.Net\\Framework64\\v4.0.30319找到aspnet.config文件(可能这也适用于更高版本)并将legacyImpersonationPolicy的值legacyImpersonationPolicy为false

<legacyImpersonationPolicy enabled="false"/>

确保重新启动IIS(或重新启动计算机)。
只要您使用托管方法进行模拟,这将使模拟流动。 在我的情况下,我冒充类似于此,这工作正常:

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

这里解释了aspnet.config设置(顺便说一句,它无法在web.config文件中设置它): http//msdn.microsoft.com/en-us/library/ms229296 (v = vs10 ) .aspx (它基本上说,如果这是真的,我们用.NET 1.1的方式)

您可以使用此方法检查窗口标识是否流动:

System.Security.SecurityContext.IsWindowsIdentityFlowSuppressed()

我不同意你的问题。

问题不是你的await 但你的Task.Run 在ASP.Net代码上应该没有await Task.Run 它的效果是一个不必要的线程切换。 由于ASP.Net上没有STA线程,因此不需要这样做,只会减慢代码速度。

如果您坚持使用真正无线程的Task那么您应该没有任何问题,因为您将保留在一个线程中。 除非您的应用程序服务器具有非常有限的客户端数量和大量的CPU绑定操作,否则多线程不适合扩展,因为单个用户可以快速填写服务器的计划。

您应该使用Task.FromResultTaskCompletionSource.Task来确保您保持单线程。 通过[ThreadLocal]属性可以解决您的问题。

TL:DR

不要在服务器端使用Task.Run 使用Task.FromResult因此您只有一个线程。

编辑:响应

哪个帖子? 在客户端,您仍将使用await 我从来没说过不要await 我说过不要直接使用await Task.Run (UI线程除外)。 我没有说你应该BLOCK一个线程。 因为你的线程应该做WORK来产生你传递给Task.FromResult的结果。 BLOCKING意味着你的线程什么都不做,同时消耗资源(即内存)。 哎呀,甚至没有必要

服务器端应该使用这种模式:

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

客户应该留下来

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

在ServiceClient本地解析的情况下,所有内容都会同步运行(速度更快,资源更少)。 这里的要点是你只是为没有异步的线程样式应用Task async模式。 Task.Run是async的并发风格,只应在需要使用另一个线程时使用(因为你是CPU绑定的,或者这个线程需要用于其他东西)。

由于我负责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();
        }
    }
}

我不得不说这是一种解决方法 - 而不是解决方案 - 它改变了服务器端的接口(仅限非Async接口),但至少它是有效的。

此解决方案的一个优点 - 您可以将模拟实现为ExampleServiceClient之上的行为模式。

暂无
暂无

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

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