0

У меня возникла сложность при обработке следующего сценария.Как загрузить boottrap для канала WCF, созданного с помощью Unity InjectionFactory

  1. Я хочу использовать Unity DI Framework для создания нового канала для моего обслуживания, когда это необходимо.
  2. Служба обеспечена защитой от несанкционированного доступа.
  3. Услуга не вызывается из службы, размещенной в IIS, но вызывается из самообслуживаемой службы WCF.

Моя текущая проблема возникает в шаге 3 выше - как я могу загружать токен и все же удовлетворять вышеуказанным требованиям? Я опишу решения для каждого шага, хотя, поскольку они нетривиальны и, надеюсь, помогут кому-то еще с этой проблемой.

Решение проблемы 1: Следующий фрагмент кода создаст новый экземпляр канала в любое время.

container.RegisterType<IMyWcfService>(new PerResolveLifetimeManager(), 
    new InjectionFactory(
     x => new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*") 
      .CreateChannel())); 

Вот некоторые примеры людей, которые делают это лучше, чем я: http://unity.codeplex.com/discussions/211736 https://gist.github.com/tracker1/5675161

Вы можете также использовать альтернативные менеджеры прижизненные, а также, например, TransientLifetimeManager будет работать здесь хорошо.

Решение вопроса 2: Теперь начинается настоящая трудность - как включить маркер безопасности с помощью InjectionFactory? Очевидно, что я хочу использовать CreatChannelWithIssuedToken, но мне нужно будет захватить токен bootstrap, чтобы сделать это. Это довольно хорошо документировано вокруг сети, например. здесь: http://www.cloudidentity.com/blog/2012/11/30/using-the-bootstrapcontext-property-in-net-4-5-2/ Некоторые важные примечания: убедитесь, что у вас есть раздел serviceBehaviors в конфиге, который указывает useIdentityConfiguration = "true", иначе ваш system.identityModel раздел будет проигнорирован, например.

<serviceBehaviors> 
    <behavior name=""> 
     <serviceCredentials useIdentityConfiguration="true" /> 
    </behavior> 
    </serviceBehaviors> 

А затем ваша система.identityModel раздел должен также особенность:

<system.identityModel> 
    <identityConfiguration> 
    <securityTokenHandlers> 
     <securityTokenHandlerConfiguration saveBootstrapContext="true" /> 
    </securityTokenHandlers> 
    </identityConfiguration> 
</system.identityModel> 

Тогда, при условии, что вы сделали запрос, чтобы правильно установить это на сессии (см мой другой вопрос: AJAX call against REST endpoint secured with Thinktecture's IdentityServer STS он будет доступен в сеансе, когда вы получаете доступ к начальной загрузки контекста маркер безопасности как так:

var bootstrapContext = ((ClaimsIdentity) Thread.CurrentPrincipal.Identity).BootstrapContext 
    as BootstrapContext; 
SecurityToken securityToken = bootstrapContext.SecurityToken; 

Вы можете либо изменить InjectionFactory выглядеть следующим образом:

container.RegisterType<IControllerConfigurationService>(new PerResolveLifetimeManager(), 
    new InjectionFactory(
     x => 
     { 
      var bootstrapContext = ((ClaimsIdentity) 
       Thread.CurrentPrincipal.Identity).BootstrapContext as BootstrapContext; 
      var securityToken = bootstrapContext.SecurityToken; 
      return new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*") 
       .CreateChannelWithIssuedToken(securityToken); 
     })); 

или на HAPS еще лучше, создать класс, который наследуется от ChannelFactory и добавить метод, который вытягивает, который сочетает в вышеупомянутую логику в единый метод CreateChannelWithIssuedTokenUsingBootstrapContext:

public class ChannelFactoryWithChannelFactoryOperations<T> : ChannelFactory<T> 
{ 
    protected ChannelFactoryWithChannelFactoryOperations(Type channelType) : base(channelType) 
    { 
    } 

    public ChannelFactoryWithChannelFactoryOperations() 
    { 
    } 

    public ChannelFactoryWithChannelFactoryOperations(string endpointConfigurationName) 
     : base(endpointConfigurationName) 
    { 
    } 

    public ChannelFactoryWithChannelFactoryOperations(string endpointConfigurationName, 
     EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress) 
    { 
    } 

    public ChannelFactoryWithChannelFactoryOperations(Binding binding) : base(binding) 
    { 
    } 

    public ChannelFactoryWithChannelFactoryOperations(Binding binding, string remoteAddress) 
     : base(binding, remoteAddress) 
    { 
    } 

    public ChannelFactoryWithChannelFactoryOperations(Binding binding, EndpointAddress remoteAddress) 
     : base(binding, remoteAddress) 
    { 
    } 

    public ChannelFactoryWithChannelFactoryOperations(ServiceEndpoint endpoint) : base(endpoint) 
    { 
    } 

    public T CreateChannelWithIssuedTokenUsingBootstrapContext() 
    { 
     var bootstrapContext = 
      ((ClaimsIdentity) Thread.CurrentPrincipal.Identity).BootstrapContext as BootstrapContext; 
     SecurityToken securityToken = bootstrapContext.SecurityToken; 
     return CreateChannelWithIssuedToken(securityToken); 
    } 
} 

Это позволяет затем просто назвать это:

container.RegisterType<IMyWcfService>(new PerResolveLifetimeManager(), 
    new InjectionFactory(
    x => new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*") 
        .CreateChannelWithIssuedTokenUsingBootstrapContext())); 

Решение проблемы 3: Добавляя сложность вышеупомянутых двух проблем, я теперь пытаюсь сделать то же самое за пределами IIS в своей собственной службе WCF с самообслуживанием. Эта же услуга полностью безгражданна, поэтому здесь возникает моя следующая дилемма: Загрузка жеста токена по-прежнему происходит, но это не происходит в правильной теме. Unity, похоже, запускает свой InjectionFactory в отдельном потоке для фактического выполнения служебного вызова.

После выполнения делегата InjectionFactory, CurrentPrincipal является несанкционированным GenericPrincipal. Это отличается от того, что было у нас в вопросе 2 выше - где это утвержденный ClaimsPrincipal - я считаю, что это все настроено на сеанс IIS (пожалуйста, не стесняйтесь исправить, если я ошибаюсь).

достаточно странно, если бы мы тогда заменить выше

container.RegisterType<IMyWcfService>(new PerResolveLifetimeManager(), 
    new InjectionFactory(
    x => new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*") 
        .CreateChannel())); 

т.е. теперь просто впрыснуть необеспеченный объект Channel, мы можем заметить, что в нашей самопринятом WCF службе, где мы на самом деле пытаемся взаимодействовать с канал, Thread.CurrentPrincipal - это аутентифицированный ClaimsPrincipal с SecurityToken, правильно загруженный по основному.

Таким образом, проблему можно охарактеризовать следующим образом: , потому что делегат InjectionFactory выполняется в потоке, по которому еще не прошла аутентификация/авторизация, SecurityToken на самом деле не доступен для перехода к созданию канала.

Есть ли у кого-нибудь предложения о том, как я могу решить эту проблему? Разве я уже нарисовал себя в углу с этим конкретным сочетанием самодостаточного WCF и единства?

Спасибо, Clint

ответ

1

Я нашел способ сделать это, хотя я хотел бы лучше предложение. Метод SessionSecurityTokenHandler ValidateToken всегда выполняется на том же потоке, что и InjectionFactory делегата, так что мы можем установить CurrentPrincipal что нить в методе ValidateToken в виде CustomSessionSecurityTokenHandler так:

public class CustomSessionSecurityTokenHandler: SessionSecurityTokenHandler 
{ 
    public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SecurityToken token) 
    { 
     var claimsIdentities = base.ValidateToken(token); 

     Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentities); 

     return claimsIdentities; 
    } 
} 

а затем система.Конфигурация identityModel должна быть изменена, чтобы включать в себя пользовательские securityTokenHandler:

<securityTokenHandlers> 
    <remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> 
    <add type="MyAssembly.CustomSessionSecurityTokenHandler, MyAssembly" /> 
    </securityTokenHandlers> 

Сделав это, пытается получить доступ к маркеру безопасности из контекста Bootstrap, то получится:

container.RegisterType<IControllerConfigurationService>(new PerResolveLifetimeManager(), 
    new InjectionFactory(
     x => 
     { 
      var bootstrapContext = ((ClaimsIdentity) 
       Thread.CurrentPrincipal.Identity).BootstrapContext as BootstrapContext; 
      var securityToken = bootstrapContext.SecurityToken; 
      return new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*") 
       .CreateChannelWithIssuedToken(securityToken); 
     })); 

Пожалуйста, не стесняйтесь добавлять альтернативы предложения! :)

Thanks, Clint

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