我在将此 SAMLResponse 读入 SecurityToken 对象时遇到问题
I am having problems reading this SAMLResponse into a SecurityToken object
我是运行一个Shibboleth SSO login page, which, when I successfully authenticate myself, does a POST with a SAMLResponse string that contains a base-64 encoded SAML response to my .NET MVC application. I am trying to parse this into a SecurityToken对象,如果可能的话:
var tokenString = Encoding.UTF8.GetString(Convert.FromBase64String(base64EncodedSamlToken));
var token = System.IdentityModel.Services.FederatedAuthentication.FederationConfiguration.IdentityConfiguration.SecurityTokenHandlers.ReadToken(new XmlTextReader(new StringReader(tokenString)));
token
的值一直是null,所以解析不正确,这是为什么呢?
在令牌的 XML 中,我在其他 XML 标签和数据中看到以下响应标签:
<saml2p:Response Destination="https://blah.local/" ID="_be8cc118c528ba0407446b8cc2dca019" InResponseTo="ID-302e3473-f063-43b0-b8e4-b8f47fbbe350" IssueInstant="2015-12-29T16:19:35.603Z" Version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://shibboleth.blah.local/idp/shibboleth</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
我的印象是内置的 SecurityTokenHandlers 可以读取这种类型的响应。不是这样吗?它不是很清楚,如 there was an extension for WIF awhile ago to allow SP-initiated SSO,但据我所知,它不能用于生产用途;虽然,link 下载它现在已经死了(就像微软的所有开发门户 link 一样,当你真正需要的时候)。我的主要理解是,这与 SAML 2.0 安全令牌的不同之处在于,这是 SAML-P 2.0,其中 P 代表 "Protocol",并且 WIF 在这么多年后仍然不完全支持 SAML 2.0 协议,但是不支持 SAML 2.0 令牌。什么情况?也许有人愿意详细说明? 更新:确实,正如其他人评论的那样,WIF 仍然不支持 SAML-P,即使经过了这么多年。
我的默认 SecurityTokenHandlers
集合包括以下不同的处理程序,其中 none 似乎想要处理此响应:
SamlSecurityTokenHandler
Saml2SecurityTokenHandler
WindowsUserNameSecurityTokenHandler
X509SecurityTokenHandler
KerberosSecurityTokenHandler
RsaSecurityTokenHandler
SessionSecurityTokenHandler
EncryptedSecurityTokenHandler
如何从此 SAMLResponse 中获取 SecurityToken?
我找到了解决办法。一旦我将 SAMLResponse 作为字符串抓取,我就可以反序列化它,但是反序列化的方法非常细致,因为编码是在构建令牌时导致 Saml2SecurityToken 构造函数失败的必经之路(这肯定是个坏蛋)。
这个想法很简单,从 XML 中获取 saml2:Assertion
标签作为一个元素,并用它来构建一个 SecurityToken
;它经过测试并适用于我的情况,所以希望它可以帮助其他人:
var samlToken = (SecurityToken)null;
var form = await Request.ReadFormAsync(); // I am using IOwinRequest here, but if you are using something else you can probably fetch this parameter from somewhere within HttpContext.Current.Request.Form["SAMLResponse"]; or elsewhere.
if (form.Count() > 0)
{
var samlResponses = form.GetValues("SAMLResponse");
if (samlResponses != null && samlResponses.Count > 0)
{
foreach (var samlResponse in samlResponses)
{
try
{
var decodedSamlResponse = Convert.FromBase64String(samlResponse);
var reader = XmlReader.Create(new MemoryStream(decodedSamlResponse));
var serializer = new XmlSerializer(typeof(XmlElement));
var samlResponseElement = (XmlElement)serializer.Deserialize(reader);
var manager = new XmlNamespaceManager(samlResponseElement.OwnerDocument.NameTable);
manager.AddNamespace("saml2", "urn:oasis:names:tc:SAML:2.0:assertion");
var assertion = (XmlElement)samlResponseElement.SelectSingleNode("//saml2:Assertion", manager);
samlToken = (Saml2SecurityToken)Options.SecurityTokenHandlers.ReadToken(XmlReader.Create(new StringReader(assertion.OuterXml)));
break;
}
catch { }
}
}
}
我是运行一个Shibboleth SSO login page, which, when I successfully authenticate myself, does a POST with a SAMLResponse string that contains a base-64 encoded SAML response to my .NET MVC application. I am trying to parse this into a SecurityToken对象,如果可能的话:
var tokenString = Encoding.UTF8.GetString(Convert.FromBase64String(base64EncodedSamlToken));
var token = System.IdentityModel.Services.FederatedAuthentication.FederationConfiguration.IdentityConfiguration.SecurityTokenHandlers.ReadToken(new XmlTextReader(new StringReader(tokenString)));
token
的值一直是null,所以解析不正确,这是为什么呢?
在令牌的 XML 中,我在其他 XML 标签和数据中看到以下响应标签:
<saml2p:Response Destination="https://blah.local/" ID="_be8cc118c528ba0407446b8cc2dca019" InResponseTo="ID-302e3473-f063-43b0-b8e4-b8f47fbbe350" IssueInstant="2015-12-29T16:19:35.603Z" Version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://shibboleth.blah.local/idp/shibboleth</saml2:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
我的印象是内置的 SecurityTokenHandlers 可以读取这种类型的响应。不是这样吗?它不是很清楚,如 there was an extension for WIF awhile ago to allow SP-initiated SSO,但据我所知,它不能用于生产用途;虽然,link 下载它现在已经死了(就像微软的所有开发门户 link 一样,当你真正需要的时候)。我的主要理解是,这与 SAML 2.0 安全令牌的不同之处在于,这是 SAML-P 2.0,其中 P 代表 "Protocol",并且 WIF 在这么多年后仍然不完全支持 SAML 2.0 协议,但是不支持 SAML 2.0 令牌。什么情况?也许有人愿意详细说明? 更新:确实,正如其他人评论的那样,WIF 仍然不支持 SAML-P,即使经过了这么多年。
我的默认 SecurityTokenHandlers
集合包括以下不同的处理程序,其中 none 似乎想要处理此响应:
SamlSecurityTokenHandler
Saml2SecurityTokenHandler
WindowsUserNameSecurityTokenHandler
X509SecurityTokenHandler
KerberosSecurityTokenHandler
RsaSecurityTokenHandler
SessionSecurityTokenHandler
EncryptedSecurityTokenHandler
如何从此 SAMLResponse 中获取 SecurityToken?
我找到了解决办法。一旦我将 SAMLResponse 作为字符串抓取,我就可以反序列化它,但是反序列化的方法非常细致,因为编码是在构建令牌时导致 Saml2SecurityToken 构造函数失败的必经之路(这肯定是个坏蛋)。
这个想法很简单,从 XML 中获取 saml2:Assertion
标签作为一个元素,并用它来构建一个 SecurityToken
;它经过测试并适用于我的情况,所以希望它可以帮助其他人:
var samlToken = (SecurityToken)null;
var form = await Request.ReadFormAsync(); // I am using IOwinRequest here, but if you are using something else you can probably fetch this parameter from somewhere within HttpContext.Current.Request.Form["SAMLResponse"]; or elsewhere.
if (form.Count() > 0)
{
var samlResponses = form.GetValues("SAMLResponse");
if (samlResponses != null && samlResponses.Count > 0)
{
foreach (var samlResponse in samlResponses)
{
try
{
var decodedSamlResponse = Convert.FromBase64String(samlResponse);
var reader = XmlReader.Create(new MemoryStream(decodedSamlResponse));
var serializer = new XmlSerializer(typeof(XmlElement));
var samlResponseElement = (XmlElement)serializer.Deserialize(reader);
var manager = new XmlNamespaceManager(samlResponseElement.OwnerDocument.NameTable);
manager.AddNamespace("saml2", "urn:oasis:names:tc:SAML:2.0:assertion");
var assertion = (XmlElement)samlResponseElement.SelectSingleNode("//saml2:Assertion", manager);
samlToken = (Saml2SecurityToken)Options.SecurityTokenHandlers.ReadToken(XmlReader.Create(new StringReader(assertion.OuterXml)));
break;
}
catch { }
}
}
}