如何在 WCF 和 WIF 中使用 JWT 令牌?
How to use JWT tokens with WCF and WIF?
一般注意事项
我们正在使用 IdentityServer3,并且到目前为止一直非常满意。
在 MS 和 Thinktecture OWIN 中间件的帮助下,我们已经非常轻松地保护 MVC 和 ASP.NET Web API 应用程序。
我们工作的客户仍然有很多 SOAP WCF 服务,这就是我们遇到困难的地方。
设置
我不会撒谎,我对 WCF 还很陌生,我只将它用于非常基本的场景 - 了解 basicHttpBinding,没有传输或消息安全。
这就是我想要实现的:
- 客户端从 IdentityServer 获取 JWT 访问令牌
- 令牌以某种方式最终出现在 SOAP 消息中 headers
- WCF 读取并验证令牌
- WCF 检查声明并根据某些标准执行授权
我无法执行第三步。
服务器设置
- 我正在使用具有
TransportWithMessageCredential
安全模式的 ws2007FederationHttpBinding
。消息包含 BearerKey
并且令牌的类型为 urn:ietf:params:oauth:token-type:jwt
- 该服务使用 WIF 身份管道,我在其中添加了来自
System.IdentityModel.Tokens.Jwt
NuGet 包的 JwtSecurityTokenHandler
客户端设置
- STS 颁发的 JWT 令牌包装在
BinarySecurityToken
XML 元素中,它本身包装在 GenericXmlSecurityElement
中
- 此令牌用作
ChannelFactory
的 CreateChannelWithIssuedToken
的参数
会发生什么
令牌在 SOAP header 中找到并传递给 JwtSecurityTokenHandler
。
但是随后抛出异常:
System.ServiceModel.Security.MessageSecurityException: Message security verification failed. ---> System.IndexOutOfRangeException: Index was outside the bounds of the array.
at System.Xml.XmlBufferReader.GetChars(Int32 offset, Int32 length, Char[] chars)
at System.Xml.XmlBufferReader.GetString(Int32 offset, Int32 length)
at System.Xml.StringHandle.GetString()
at System.Xml.XmlBaseReader.ReadEndElement()
at System.ServiceModel.Security.ReceiveSecurityHeader.ExecuteFullPass(XmlDictionaryReader reader)
at System.ServiceModel.Security.ReceiveSecurityHeader.Process(TimeSpan timeout, ChannelBinding channelBinding, ExtendedProtectionPolicy extendedProtectionPolicy)
at System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessageCore(Message& message, TimeSpan timeout)
at System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessage(Message& message, TimeSpan timeout)
--- End of inner exception stack trace ---
JustDecompiling 后,进一步读取 SOAP header 中的 XML 元素时似乎有错误。奇怪的是 token 是最后一个元素。整个消息如下所示:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/IService/GetListOfStrings</a:Action>
<a:MessageID>urn:uuid:5c22d4e2-f9b8-451a-b4ca-a844f41f7231</a:MessageID>
<ActivityId CorrelationId="554fc496-7c47-4063-9539-d25606f186b0" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">1213dcd7-55b7-4153-8a6d-92e0922f76dd</ActivityId>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo90CpMlUwLBOmEPkZ5C8fRQAAAAAVWkkf2rJS0qImBv+Yx1recUXdbBLjThDkAMkwfW3/2AACQAA</VsDebuggerCausalityData>
<a:To s:mustUnderstand="1">https://localhost.fiddler:44322/Service.svc</a:To>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="_0">
<u:Created>2015-05-21T06:41:45.362Z</u:Created>
<u:Expires>2015-05-21T06:46:45.362Z</u:Expires>
</u:Timestamp>
<wsse:BinarySecurityToken ValueType="urn:ietf:params:oauth:token-type:jwt" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><!-- Removed --></wsse:BinarySecurityToken>
</o:Security>
</s:Header>
<s:Body>
<GetListOfStrings xmlns="http://tempuri.org/" />
</s:Body>
</s:Envelope>
看起来没有什么格式不正确之类的。从堆栈跟踪中,读取 </o:Security>
结束元素时必须抛出异常,因为已正确读取和处理令牌。
复制
我分叉了samples repo所以如果你喜欢的话可以看看。
以下是相关项目:
SelfHost (Minimal)
在 sources
文件夹中。这是STS
- 在
Clients
解决方案中,WCF 服务位于 APIs
文件夹中
- 在
Clients
解决方案中,WCF 客户端是 Console Client Credentials With Wcf
项目
启动它的最佳方法是先启动 STS,然后 Right click -> Debug -> Start new instance
在 WCF 服务上,然后在 WCF 客户端上同样如此。
提前致谢!
我没能解决这个问题,但 IdentityServer 的开发者之一 Dominick Baier 找到了解决方法。
他认为异常来自 WCF 中的错误或 WCF 与 JwtSecurityTokenHandler
之间的不兼容。由于他认为 WCF 完成,因此他不希望有人看一下。
他的解决方案是将 JWT 令牌包装在 SAML 令牌中。然后,通过子类化 SamlSecurityTokenHandler
,将其取回并针对 JwtSecurityTokenHandler
.
的实例对其进行验证
链接如下:
大家玩得开心,现在:-)
一般注意事项
我们正在使用 IdentityServer3,并且到目前为止一直非常满意。
在 MS 和 Thinktecture OWIN 中间件的帮助下,我们已经非常轻松地保护 MVC 和 ASP.NET Web API 应用程序。
我们工作的客户仍然有很多 SOAP WCF 服务,这就是我们遇到困难的地方。
设置
我不会撒谎,我对 WCF 还很陌生,我只将它用于非常基本的场景 - 了解 basicHttpBinding,没有传输或消息安全。
这就是我想要实现的:
- 客户端从 IdentityServer 获取 JWT 访问令牌
- 令牌以某种方式最终出现在 SOAP 消息中 headers
- WCF 读取并验证令牌
- WCF 检查声明并根据某些标准执行授权
我无法执行第三步。
服务器设置
- 我正在使用具有
TransportWithMessageCredential
安全模式的ws2007FederationHttpBinding
。消息包含BearerKey
并且令牌的类型为urn:ietf:params:oauth:token-type:jwt
- 该服务使用 WIF 身份管道,我在其中添加了来自
System.IdentityModel.Tokens.Jwt
NuGet 包的JwtSecurityTokenHandler
客户端设置
- STS 颁发的 JWT 令牌包装在
BinarySecurityToken
XML 元素中,它本身包装在GenericXmlSecurityElement
中
- 此令牌用作
ChannelFactory
的
CreateChannelWithIssuedToken
的参数
会发生什么
令牌在 SOAP header 中找到并传递给 JwtSecurityTokenHandler
。
但是随后抛出异常:
System.ServiceModel.Security.MessageSecurityException: Message security verification failed. ---> System.IndexOutOfRangeException: Index was outside the bounds of the array.
at System.Xml.XmlBufferReader.GetChars(Int32 offset, Int32 length, Char[] chars)
at System.Xml.XmlBufferReader.GetString(Int32 offset, Int32 length)
at System.Xml.StringHandle.GetString()
at System.Xml.XmlBaseReader.ReadEndElement()
at System.ServiceModel.Security.ReceiveSecurityHeader.ExecuteFullPass(XmlDictionaryReader reader)
at System.ServiceModel.Security.ReceiveSecurityHeader.Process(TimeSpan timeout, ChannelBinding channelBinding, ExtendedProtectionPolicy extendedProtectionPolicy)
at System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessageCore(Message&amp; message, TimeSpan timeout)
at System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessage(Message&amp; message, TimeSpan timeout)
--- End of inner exception stack trace ---
JustDecompiling 后,进一步读取 SOAP header 中的 XML 元素时似乎有错误。奇怪的是 token 是最后一个元素。整个消息如下所示:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/IService/GetListOfStrings</a:Action>
<a:MessageID>urn:uuid:5c22d4e2-f9b8-451a-b4ca-a844f41f7231</a:MessageID>
<ActivityId CorrelationId="554fc496-7c47-4063-9539-d25606f186b0" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">1213dcd7-55b7-4153-8a6d-92e0922f76dd</ActivityId>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo90CpMlUwLBOmEPkZ5C8fRQAAAAAVWkkf2rJS0qImBv+Yx1recUXdbBLjThDkAMkwfW3/2AACQAA</VsDebuggerCausalityData>
<a:To s:mustUnderstand="1">https://localhost.fiddler:44322/Service.svc</a:To>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="_0">
<u:Created>2015-05-21T06:41:45.362Z</u:Created>
<u:Expires>2015-05-21T06:46:45.362Z</u:Expires>
</u:Timestamp>
<wsse:BinarySecurityToken ValueType="urn:ietf:params:oauth:token-type:jwt" EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><!-- Removed --></wsse:BinarySecurityToken>
</o:Security>
</s:Header>
<s:Body>
<GetListOfStrings xmlns="http://tempuri.org/" />
</s:Body>
</s:Envelope>
看起来没有什么格式不正确之类的。从堆栈跟踪中,读取 </o:Security>
结束元素时必须抛出异常,因为已正确读取和处理令牌。
复制
我分叉了samples repo所以如果你喜欢的话可以看看。 以下是相关项目:
SelfHost (Minimal)
在sources
文件夹中。这是STS- 在
Clients
解决方案中,WCF 服务位于APIs
文件夹中 - 在
Clients
解决方案中,WCF 客户端是Console Client Credentials With Wcf
项目
启动它的最佳方法是先启动 STS,然后 Right click -> Debug -> Start new instance
在 WCF 服务上,然后在 WCF 客户端上同样如此。
提前致谢!
我没能解决这个问题,但 IdentityServer 的开发者之一 Dominick Baier 找到了解决方法。
他认为异常来自 WCF 中的错误或 WCF 与 JwtSecurityTokenHandler
之间的不兼容。由于他认为 WCF 完成,因此他不希望有人看一下。
他的解决方案是将 JWT 令牌包装在 SAML 令牌中。然后,通过子类化 SamlSecurityTokenHandler
,将其取回并针对 JwtSecurityTokenHandler
.
链接如下:
大家玩得开心,现在:-)