如何为使用Unity InjectionFactory创建的WCF通道引导securitytoken

如何为使用Unity InjectionFactory创建的WCF通道引导securitytoken,wcf,federated-identity,sts-securitytokenservice,thinktecture-ident-server,Wcf,Federated Identity,Sts Securitytokenservice,Thinktecture Ident Server,我在处理以下场景时遇到了很大的挑战 我想在需要时使用Unity DI框架为我的服务创建一个新的通道 该服务使用联邦安全性进行保护 该服务不是从IIS中托管的服务中调用的,而是从自托管的WCF服务中调用的 我当前的问题发生在上面的步骤3中-我如何引导令牌,然后又满足上述2个要求??不过,我将概述每个步骤的解决方案,因为它们并不琐碎,希望能帮助其他人解决这个问题 解决问题1: 以下代码段将在需要时创建通道的新实例 container.RegisterType<IMyWcfService>

我在处理以下场景时遇到了很大的挑战

  • 我想在需要时使用Unity DI框架为我的服务创建一个新的通道
  • 该服务使用联邦安全性进行保护
  • 该服务不是从IIS中托管的服务中调用的,而是从自托管的WCF服务中调用的
  • 我当前的问题发生在上面的步骤3中-我如何引导令牌,然后又满足上述2个要求??不过,我将概述每个步骤的解决方案,因为它们并不琐碎,希望能帮助其他人解决这个问题

    解决问题1: 以下代码段将在需要时创建通道的新实例

    container.RegisterType<IMyWcfService>(new PerResolveLifetimeManager(),
        new InjectionFactory(
            x => new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
                .CreateChannel()));
    
    然后,您可以将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);
            }));
    
    container.RegisterType(新的PerResolveLifetimeManager(),
    新注射厂(
    x=>
    {
    var bootstrapContext=((索赔实体)
    作为BootstrapContext的BootstrapContext;
    var securityToken=bootstrapContext.securityToken;
    返回新的ChannelFactoryWithChannelFactoryOperations(“*”)
    .CreateChannelWithIssuedToken(securityToken);
    }));
    
    或者更好的方法是,创建一个继承自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);
        }
    }
    
    公共类ChannelFactoryWithChannelFactoryOperations:ChannelFactory
    {
    受保护的ChannelFactoryWithChannelFactoryOperations(类型channelType):基本(channelType)
    {
    }
    公共渠道工厂WithChannelFactoryOperations()
    {
    }
    公共ChannelFactoryWithChannelFactoryOperations(字符串endpointConfigurationName)
    :base(endpointConfigurationName)
    {
    }
    公共ChannelFactoryWithChannelFactoryOperations(字符串endpointConfigurationName,
    EndpointAddress remoteAddress):基(endpointConfigurationName,remoteAddress)
    {
    }
    public ChannelFactoryWithChannelFactoryOperations(绑定):基本(绑定)
    {
    }
    公共ChannelFactoryWithChannelFactoryOperations(绑定、字符串远程地址)
    :base(绑定,远程地址)
    {
    }
    公共ChannelFactoryWithChannelFactoryOperations(绑定绑定、EndpointAddress remoteAddress)
    :base(绑定,远程地址)
    {
    }
    公共ChannelFactoryWithChannelFactoryOperations(ServiceEndpoint端点):基(端点)
    {
    }
    公共T CreateChannelWithIssuedTokenUsingBootstrapContext()创建通道
    {
    var bootstrapContext=
    ((ClaimsIdentity)Thread.CurrentPrincipal.Identity).BootstrapContext作为BootstrapContext;
    SecurityToken SecurityToken=bootstrapContext.SecurityToken;
    返回CreateChannelWithIssuedToken(securityToken);
    }
    }
    
    这样,您就可以将其称为:

    container.RegisterType<IMyWcfService>(new PerResolveLifetimeManager(),
        new InjectionFactory(
        x => new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
                        .CreateChannelWithIssuedTokenUsingBootstrapContext()));
    
    container.RegisterType(新的PerResolveLifetimeManager(),
    新注射厂(
    x=>new ChannelFactoryWithChannelFactoryOperations(“*”)
    .CreateChannelWithIssuedTokenUsingBootstrapContext());
    
    解决问题3: 再加上以上两个问题的复杂性,我现在正在自己的自托管WCF服务中尝试在IIS之外的同样的事情。同样的服务是完全无状态的,所以我的下一个难题出现在这里: 安全令牌的引导仍然会发生,但它没有在正确的线程上发生。Unity似乎在实际服务调用执行的单独线程中运行其InjectionFactory

    i、 e.当执行上述InjectionFactory委托时,CurrentPrincipal是未经授权的GenericPrincipal。这与我们在上面第2期中所做的不同——这是一个授权的ClaimsPrincipal——我相信这都是由IIS会话设置的(如果我不正确,请随时更正)

    有趣的是,如果我们用

    container.RegisterType<IMyWcfService>(new PerResolveLifetimeManager(),
        new InjectionFactory(
        x => new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
                        .CreateChannel()));
    
    container.RegisterType(新的PerResolveLifetimeManager(),
    新注射厂(
    x=>new ChannelFactoryWithChannelFactoryOperations(“*”)
    .CreateChannel());
    
    i、 e.现在只要注入一个不安全的通道对象,我们就可以观察到,在我们的自托管WCF服务中,我们实际上尝试与通道交互,Thread.CurrentPrincipal是经过身份验证的ClaimsPrincipal,SecurityToken正确引导到主体上

    因此,问题可以总结如下: 由于InjectionFactory委托在尚未进行身份验证/授权的线程上执行,因此SecurityToken实际上无法传递给通道的创建

    有人对我如何解决这个问题有什么建议吗?我是否已经把自己画进了一个角落,把自己与WCF和unity结合在一起

    谢谢,
    克林特

    我已经找到了一种方法来做这件事,不过我希望有一个更好的建议。SessionSecurityTokenHandler ValidateToken方法始终在与InjectionFactory委托相同的线程上执行,因此我们可以在CustomSessionSecurityTokenHandler的ValidateToken方法中设置该线程的CurrentPrincipal,如下所示:

    public class CustomSessionSecurityTokenHandler: SessionSecurityTokenHandler
    {
        public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SecurityToken token)
        {
            var claimsIdentities = base.ValidateToken(token);
    
            Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentities);
    
            return claimsIdentities;
        }
    }
    
    公共类CustomSessionSecurityTokenHandler:SessionSecurityTokenHandler
    {
    公共重写只读集合ValidateToken(SecurityToken令牌)
    {
    var claimsIdentities=base.ValidateToken(令牌);
    Thread.CurrentPrincipal=新的ClaimsPrincipal(索赔实体);
    返还请求权;
    }
    }
    
    然后需要修改system.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);
            }));
    
    container.Regi
    
    public class CustomSessionSecurityTokenHandler: SessionSecurityTokenHandler
    {
        public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SecurityToken token)
        {
            var claimsIdentities = base.ValidateToken(token);
    
            Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentities);
    
            return claimsIdentities;
        }
    }
    
      <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);
            }));