我在将此SAMLResponse读取到SecurityToken对象时遇到问题

本文关键字:对象 SecurityToken 遇到 问题 读取 SAMLResponse | 更新日期: 2023-09-27 18:28:21

我正在运行一个Shibboleth SSO登录页面,当我成功验证自己时,该页面会使用SAMLResponse字符串执行POST,该字符串包含对我的.NET MVC应用程序的基于64编码的SAML响应。如果可能的话,我正在尝试将其解析为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可以读取这种类型的响应。事实并非如此吗?这不是很清楚,因为不久前有一个WIF的扩展,允许SP启动的SSO,但据我所知,它不可用于生产用途;尽管如此,下载它的链接现在已经失效(就像你真正需要的时候微软的所有开发门户链接一样)。我的主要理解是,这与SAML 2.0安全令牌的不同之处在于,这是SAML-P 2.0,其中P代表"协议",而且经过这么多年,WIF仍然不完全支持SAML 2.0协议,但确实支持SAML 2.0令牌。情况如何?也许有人想详细说明更新:事实上,正如其他人所评论的,即使经过这么多年,SAML-P在WIF中仍然不受支持

我的默认SecurityTokenHandlers集合包括以下不同的处理程序,它们似乎都不想处理此响应:

SamlSecurityTokenHandler
Saml2SecurityTokenHandler
WindowsUserNameSecurityTokenHandler
X509SecurityTokenHandler
KerberosSecurityTokenHandler
RsaSecurityTokenHandler
SessionSecurityTokenHandler
EncryptedSecurityTokenHandler

如何从该SAML响应中获取SecurityToken?

我在将此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 { }
        }
    }
}