首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何引导使用统一InjectionFactory创建的WCF通道的安全令牌

如何引导使用统一InjectionFactory创建的WCF通道的安全令牌
EN

Stack Overflow用户
提问于 2014-02-11 04:46:27
回答 1查看 889关注 0票数 0

在处理下面的场景时,我遇到了相当大的挑战。

  1. 我希望在需要时使用Unity框架为我的服务创建一个新的通道。
  2. 该服务使用联邦安全保护。
  3. 服务不是从IIS托管的服务中调用的,而是从自托管WCF服务中调用的。

我当前的问题发生在上面的步骤3中--我如何引导令牌,然后满足上述2项要求??不过,我将概述每个步骤的解决方案,因为它们不是微不足道的,希望能在这个问题上帮助其他人。

解决问题1:以下代码片段将在任何需要的时候创建一个新的通道实例。

代码语言:javascript
复制
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/一些重要的事情要注意:确保您在配置中有一个指定useIdentityConfiguration="true“的serviceBehaviors节,否则您的system.identityModel部分将被忽略。

代码语言:javascript
复制
  <serviceBehaviors>
    <behavior name="">
      <serviceCredentials useIdentityConfiguration="true" />
    </behavior>
  </serviceBehaviors>

然后,您的system.identityModel部分还应该提供以下功能:

代码语言:javascript
复制
<system.identityModel>
  <identityConfiguration>
    <securityTokenHandlers>
      <securityTokenHandlerConfiguration saveBootstrapContext="true" />
    </securityTokenHandlers>
  </identityConfiguration>
</system.identityModel>

然后,如果您已经请求在会话上正确设置它(请参阅我的另一个问题:AJAX call against REST endpoint secured with Thinktecture's IdentityServer STS ),那么每当您访问引导上下文安全令牌时,它将在会话中可用,如下所示:

代码语言:javascript
复制
var bootstrapContext = ((ClaimsIdentity) Thread.CurrentPrincipal.Identity).BootstrapContext 
    as BootstrapContext;
SecurityToken securityToken = bootstrapContext.SecurityToken;

然后,您可以将InjectionFactory更改为如下所示:

代码语言:javascript
复制
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);
        }));

或者更好的方法是创建一个继承自的类,并添加一个将上述逻辑组合到单个ChannelFactory方法中的方法:

代码语言:javascript
复制
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);
    }
}

这样你就可以把它叫做:

代码语言:javascript
复制
container.RegisterType<IMyWcfService>(new PerResolveLifetimeManager(),
    new InjectionFactory(
    x => new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
                    .CreateChannelWithIssuedTokenUsingBootstrapContext()));

解决问题3:增加上述两个问题的复杂性,我现在在我自己托管的WCF服务中尝试IIS之外的相同内容。同样的服务是完全无状态的,所以下面是我的下一个两难处境:安全令牌的引导仍然在发生,但是它没有发生在正确的线程上。统一似乎在一个单独的线程中运行它的InjectionFactory到实际的服务调用执行。

也就是说,当上面的InjectionFactory委托执行时,CurrentPrincipal是一个未经授权的GenericPrincipal。这与我们在上面第2期(它是一个授权的ClaimsPrincipal )中的内容不同,我相信这都是由IIS会话设置的(如果我不正确,请随时更正)。

有趣的是,如果我们用

代码语言:javascript
复制
container.RegisterType<IMyWcfService>(new PerResolveLifetimeManager(),
    new InjectionFactory(
    x => new ChannelFactoryWithChannelFactoryOperations<IMyWcfService>("*")
                    .CreateChannel()));

也就是说,现在只要注入一个不安全的信道对象,我们就可以观察到,在我们自己托管的WCF服务中,我们实际上尝试与通道交互,Thread.CurrentPrincipal是经过身份验证的ClaimsPrincipal,在主体上正确引导SecurityToken。

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

有人对我如何解决这个问题有任何建议吗?我是否已经把自己画到了一个角落,与这种自我托管的WCF和统一的特殊组合?

谢谢,克林特

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-02-11 05:00:56

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

代码语言:javascript
复制
public class CustomSessionSecurityTokenHandler: SessionSecurityTokenHandler
{
    public override ReadOnlyCollection<ClaimsIdentity> ValidateToken(SecurityToken token)
    {
        var claimsIdentities = base.ValidateToken(token);

        Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentities);

        return claimsIdentities;
    }
}

然后,需要修改system.identityModel配置以包括自定义securityTokenHandler:

代码语言:javascript
复制
  <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上下文访问安全令牌,然后成功:

代码语言:javascript
复制
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);
        }));

请随时加入其他建议!)

谢谢,克林特

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21693339

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档