0

我在處理以下場景中遇到了相當大的挑戰。如何引導使用Unity InjectionFactory創建的WCF通道的securitytoken

  1. 我想在需要時使用Unity DI Framework爲我的服務創建一個新通道。
  2. 該服務通過聯合安全進行保護。
  3. 服務不是從託管在IIS中的服務中調用的,而是從自託管的WCF服務中調用的。

我目前的問題發生在上面的第3步 - 我如何引導令牌,然後滿足上述2的要求?我將概述每個步驟的解決方案,因爲它們不重要,並且希望可以幫助其他人解決此問題。

解決問題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,但我需要抓取引導令牌來執行此操作。這在網絡周圍有相當好的記錄,例如,在這裏: 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: 添加上述兩個問題的複雜性後,我現在在自己的自託管WCF服務中嘗試在IIS之外使用同樣的東西。這個相同的服務是完全無狀態的,所以這裏是我的下一個難題發生的地方: 安全令牌的引導仍然存在,但它不在正確的線程上發生。 Unity似乎在一個單獨的Thread中將其InjectionFactory運行到實際的服務調用執行。

即當上面的InjectionFactory委託執行時,CurrentPrincipal是未授權的GenericPrincipal。這與我們在上面問題2中得到的不同 - 它是授權的ClaimsPrincipal - 我相信這全部由IIS會話設置(如果我不正確,請隨時糾正)。

有趣的是,如果我們再更換以上

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

即現在只是注入一個不安全的通道對象,我們可以看到,在我們的自承載WCF服務,我們真正嘗試與互動通道,Thread.CurrentPrincipal是經過身份驗證的ClaimsPrincipal,其中SecurityToken在主體上正確引導。

所以這個問題可以概括如下: 因爲InjectionFactory委託在還沒有發生認證/授權的線程上執行,所以SecurityToken實際上並不可用於傳遞給通道的創建。

有沒有人對我如何解決這個問題有任何建議?我是否已經將自己託管的WCF和團結的這種特殊組合融合到了一個角落?

感謝, 克林特

回答

1

我已經找到一種方法來做到這一點,但我喜歡一個更好的建議。該SessionSecurityTokenHandler ValidateToken方法始終執行的同一個線程中InjectionFactory代表,所以我們可以設置線程的CurrentPrincipal在CustomSessionSecurityTokenHandler的ValidateToken方法,像這樣:

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> 

已經做到這一點,試圖訪問從引導背景下的安全令牌,然後成功:

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

請隨意添加任何替代建議! :)

謝謝, 克林特