如何为使用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>
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);
}));