Spring Security SAML 单点登录全局单点注销LogoutRequest解析问题

Posted

技术标签:

【中文标题】Spring Security SAML 单点登录全局单点注销LogoutRequest解析问题【英文标题】:Spring Security SAML One Login Global Single Logout LogoutRequest Parsing Issue 【发布时间】:2016-02-29 03:03:17 【问题描述】:

我正在使用 One Login 实现 Spring Security SAML。我已经设置了所有配置文件并设置了元数据。

如果我从我登录的同一个应用程序注销,我能够进行登录工作并且注销工作正常。在这种情况下,来自 SAML IDP 我得到 LogoutResponse 并且 Spring Security 能够解析和处理它。

http://localhost:8080/web/saml/SingleLogout?SAMLResponse=........

问题是当我登录两个应用程序时,目前我登录到 One Login 管理控制台,有一个指向我的应用程序的链接,我点击它,我可以直接登录我的应用程序,现在当我从一个登录管理控制台,我的应用程序获得 LogoutRequest

http://localhost:8080/web/saml/SingleLogout?SAMLRequest=............

Spring Security 很好地解析它并将对象传递给验证检查逻辑。

org.springframework.security.saml.websso.processLogoutRequest(SAMLMessageContext context, SAMLCredential credential)

此方法有以下检查。

// Make sure request was authenticated if required, authentication is done as part of the binding processing
        if (!context.isInboundSAMLMessageAuthenticated() && context.getLocalExtendedMetadata().isRequireLogoutRequestSigned()) 
            throw new SAMLStatusException(StatusCode.REQUEST_DENIED_URI, "LogoutRequest is required to be signed by the entity policy");
        

我尝试跟踪跟踪,但上下文对象的字段 inboundSAMLMessageAuthenticated 从未设置为 true。上述检查失败,抛出异常。

在调试模式下,我明确地将值设为 true,它继续执行,但还有一个问题。

在同样的方法中还有另一个检查。

try 
            // Fail if NameId doesn't correspond to the currently logged user
            NameID nameID = getNameID(context, logoutRequest);
            if (nameID == null || !equalsNameID(credential.getNameID(), nameID)) 
                throw new SAMLStatusException(StatusCode.UNKNOWN_PRINCIPAL_URI, "The requested NameID is invalid");
            
         catch (DecryptionException e) 
            throw new SAMLStatusException(StatusCode.RESPONDER_URI, "The NameID can't be decrypted", e);
        

equalsNameId方法如下。

private boolean equalsNameID(NameID a, NameID b) 
        boolean equals = !differ(a.getSPProvidedID(), b.getSPProvidedID());
        equals = equals && !differ(a.getValue(), b.getValue());
        equals = equals && !differ(a.getFormat(), b.getFormat());
        equals = equals && !differ(a.getNameQualifier(), b.getNameQualifier());
        equals = equals && !differ(a.getSPNameQualifier(), b.getSPNameQualifier());
        equals = equals && !differ(a.getSPProvidedID(), b.getSPProvidedID());
        return equals;
    

这里失败了(a.getFormat(), b.getFormat())

问题

我不确定我是否遗漏了什么,有点迷失在哪里检查以解决这个问题。

我的单一注销绑定是 HTTP-Redirect

如果提供指针将不胜感激。如果需要更多信息,请告诉我。

感谢您的时间。

堆栈(旧版应用程序):

春季 3.0.6

Spring 安全 3.1.2

Spring 安全 SAML 1.0.0

Tomcat 7.x

【问题讨论】:

你有没有想过这个问题?我今天遇到了同样的事情,这是我能找到的关于这个主题的唯一问题。 【参考方案1】:

在原帖发布两年后遇到了这个问题,我不得不做一些进一步的研究。关于 SAML 规范,我仍然需要阅读一些内容,但我想我在 SAML 2.0 Errata 中找到了关于 NameIDType 结构的条目,该结构是 NameID 和 Issuer 的基础。此类型中的所有四个元素都是可选的。 OneLogin 似乎遵循此文档,并且未在 SingleLogout 请求中发送 NameID.Format。

因此,inboundMessage 的 nameID 格式为空,而凭证的 nameID 格式为“urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress”。这就是导致 getFormat 行返回 false 并未能通过整个检查的原因。

【讨论】:

以上是关于Spring Security SAML 单点登录全局单点注销LogoutRequest解析问题的主要内容,如果未能解决你的问题,请参考以下文章

使用 Grails Spring Security Saml 插件

Spring - 如何使用 SAML 2.0 实现单点登录

单点登录服务的查询字符串参数的 Spring SAML 扩展条

由于状态消息为空,ADFS 响应失败时的 Spring SAML 单点登录

更改 Spring Security SAML2 登录的 URL

Spring security SAML:自己的登录页面而不是 ADFS 登录重定向