我在处理以下情况时遇到了相当大的挑战。
我目前的问题发生在上面的第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>
然后你的system.identityModel部分也应该有:
<system.identityModel>
<identityConfiguration>
<securityTokenHandlers>
<securityTokenHandlerConfiguration saveBootstrapContext="true" />
</securityTokenHandlers>
</identityConfiguration>
</system.identityModel>
然后,如果您已经请求在会话上正确设置此项(请参阅我的另一个问题:AJAX call against REST endpoint secured with Thinktecture's IdentityServer STS,只要您访问引导上下文安全令牌,它就会在会话中可用: / p>
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);
}));
或者更好的是,创建一个继承自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似乎在一个单独的线程中运行它的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正确地在主体上进行Bootstrap。
所以问题可归纳如下: 因为InjectionFactory委托在尚未进行身份验证/授权的线程上执行,所以SecurityToken实际上无法传递给通道的创建。
有人对我如何解决这个问题有任何建议吗?我是否已经将自己与自我托管的WCF和团结的特殊组合描绘成一个角落?
谢谢, 奇
答案 0 :(得分:1)
我找到了一种方法来做到这一点,虽然我喜欢更好的建议。 SessionSecurityTokenHandler ValidateToken方法总是在与InjectionFactory委托相同的Thread上执行,因此我们可以在CustomSessionSecurityTokenHandler的ValidateToken方法中设置Thread的CurrentPrincipal,如下所示:
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:
<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);
}));
请随意添加任何其他建议! :)
谢谢, 奇