2014-02-07 4 views
6

У меня есть служба WCF, работающая на сервере, которая настроена на прием аутентификации Kerberos.Вызов службы async WCF при выдаче олицетворения

Kerberos отлично работает, поэтому служба WCF знает, какой пользователь подключается к нему. Служба предлагает все как методы Async. Как здесь (просто пример для ясности).

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

На стороне клиента У меня есть контроллер (что это 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 (как это может быть сделано с культурой в этом случае), но это не работает вообще (ну, это просто не олицетворяет себя).

ответ

5

ОК - после более глубоких исследований я наконец нашел решение, как передавать выданные идентификаторы окон в асинхронных задачах.

Решение является машинным и будет установлено для всех (в данном случае) 64-битных приложений ASP.NET 4.5.

Найти aspnet.config файл в C:\Windows\Microsoft.Net\Framework64\v4.0.30319 (вероятно, это будет применяться для более поздних версий, тоже) и изменить значение legacyImpersonationPolicy ложной

<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=vs.110).aspx (это в основном говорит, если это правда, мы делаем это в .NET 1.1 путь)

Вы можете проверить, если идентификационные данные окна пропускают или не с помощью этого метода:

System.Security.SecurityContext.IsWindowsIdentityFlowSuppressed() 
0

Поскольку я отвечал за 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(); 
     } 
    } 
} 

Я должен сказать, что это является обходным решением, а не решением, и он изменяет интерфейсы на стороне сервера (только для не-асинхронных интерфейсов), но по крайней мере он работает.

Один плюс для этого решения - вы можете реализовать олицетворение как образец поведения поверх ExampleServiceClient.

3

Я не согласен с вашим вопросом.

Проблема не в вашем await. Но ваш Task.Run. На самом деле не должно быть await Task.Run на вас код ASP.Net. Эффект от этого - ненужный переключатель нити. Поскольку в ASP.Net нет потоков STA, нет необходимости в этом, и это просто замедляет ваш код.

Если вы придерживаетесь реального безболезненного Task s, у вас не должно возникнуть никаких проблем, так как вы останетесь в одном потоке. Если у вашего сервера приложений очень ограниченное количество клиентов и огромное количество операций с привязкой к процессору, многопоточность плохо для масштабирования, поскольку один пользователь может быстро заполнить расписание вашего сервера.

Вы действительно должны использовать Task.FromResult, или TaskCompletionSource.Task, чтобы быть уверенным, что вы остаетесь однопоточным. Которая, кстати, устранит вашу проблему с помощью свойств [ThreadLocal].

TL: DR

Не используйте Task.Run на серверной стороне. Используйте Task.FromResult, поэтому у вас есть только один поток.

EDIT: Response

Какой поток? На стороне клиента вы все равно будете использовать await. Я никогда не говорил, что не использую await. Я сказал, что НЕ используйте await непосредственно с Task.Run (за исключением потока пользовательского интерфейса). Я не сказал, что вы должны БЛОКИРОВАТЬ нить. Поскольку ваш поток должен делать WORK, чтобы получить результат, который вы передаете в Task.FromResult. БЛОКИРОВАНИЕ означает, что нить ничего не делает, потребляя ресурсы (а именно, память). Черт, нет даже необходимости

стороне сервера должен использовать этот шаблон:

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 решает на месте, все работает синхронно (быстрее и с меньшими ресурсами). Дело здесь в том, что вы используете только шаблон Taskasync для стиля асинхронного использования There is no thread. Task.Run - это стиль параллелизма async и должен использоваться только тогда, когда вам нужно использовать другой поток (либо потому, что вы привязаны к ЦП, либо ЭТОТ ПОТРЕБНОСТИ потока, которые будут использоваться для чего-то еще).

+0

Это означает, что я должен блокировать поток до 5 секунд, чем использовать async и ждать? – TGlatzer

+0

Нет. Никакая блокировка не должна появляться. Но в то же время переключение нитей не должно происходить излишне. Вы должны прочитать [Stephen Cleary о том, как использовать 'async' и' Task'] (http://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html) – Aron

+0

А теперь я понимаю ваша точка, но Task.Run на стороне сервера было просто сокращенным объяснением. Но вы, вероятно, правы в этот момент. Но мой вопрос заключается не в стороне сервера - олицетворение терпит неудачу (или не протекает) на 'await' на стороне клиента. – TGlatzer

Смежные вопросы