HTTP 状态 401 - 身份验证失败:解码传入 SAML 消息时出错

Posted

技术标签:

【中文标题】HTTP 状态 401 - 身份验证失败:解码传入 SAML 消息时出错【英文标题】:HTTP Status 401 - Authentication Failed: Error decoding incoming SAML message 【发布时间】:2017-05-05 10:34:37 【问题描述】:

我按照spring saml tutorial 中的描述做了所有事情,除了我添加了

<dependency>
    <groupId>xml-apis</groupId>
    <artifactId>xml-apis</artifactId>
    <version>1.4.01</version>
</dependency>

在 pom.xml 中克服 w3.dom 类未找到问题。当我运行示例项目时,我得到了 HTTP Status 401 - Authentication Failed: Error decoding incoming SAML message 在浏览器中,同时在控制台中 (CertPathPKIXTrustEvaluator.java:81) ERROR org.springframework.security.saml.trust.MetadataCredentialResolver - PKIX path construction failed for untrusted credential: [subjectName='CN=idp.ssocircle.com' |credential entityID='https://idp.ssocircle.com']: unable to find valid certification path to requested target

包流屏幕截图:

第 2 天: 我运行keytool -importcert -alias identtrustca -file ca.cer -keystore samlKeystore.jks 并用新生成的替换security/samlKeystore.jkssecurityContext.xml

<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
    <constructor-arg value="classpath:security/samlKeystore.jks"/>
    <constructor-arg type="java.lang.String" value="nalle123"/>
    <constructor-arg>
        <map>
            <entry key="apollo" value="nalle123"/>
        </map>
    </constructor-arg>
    <constructor-arg type="java.lang.String" value="apollo"/>
</bean>

那么我有:

Caused by: java.io.IOException: Keystore was tampered with, or password was incorrect
    at sun.security.provider.JavaKeyStore.engineLoad(Unknown Source)
    at sun.security.provider.JavaKeyStore$JKS.engineLoad(Unknown Source)
    at java.security.KeyStore.load(Unknown Source)
    at org.springframework.security.saml.key.JKSKeyManager.initialize(JKSKeyManager.java:117)
    ... 57 more
Caused by: java.security.UnrecoverableKeyException: Password verification failed
    ... 61 more

然后我把密码改成我使用keytool时输入的密码:

<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
    <constructor-arg value="classpath:security/samlKeystore.jks"/>
    <constructor-arg type="java.lang.String" value="123456"/>
    <constructor-arg>
        <map>
            <entry key="apollo" value="123456"/>
        </map>
    </constructor-arg>
    <constructor-arg type="java.lang.String" value="apollo"/>
</bean>

然后控制台说:

Servlet.service() for servlet [jsp] in context with path [/spring-security-saml2-sample] threw exception
java.lang.RuntimeException: Key for alias apollo not found
    at org.springframework.security.saml.metadata.MetadataGenerator.getServerKeyInfo(MetadataGenerator.java:201)

所以我将apollo 更改为identtrustca

<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
    <constructor-arg value="classpath:security/samlKeystore.jks"/>
    <constructor-arg type="java.lang.String" value="123456"/>
    <constructor-arg>
        <map>
            <entry key="identtrustca" value="123456"/>
        </map>
    </constructor-arg>
    <constructor-arg type="java.lang.String" value="identtrustca"/>
</bean>

控制台说:

Servlet.service() for servlet [jsp] in context with path [/spring-security-saml2-sample] threw exception
java.lang.UnsupportedOperationException: trusted certificate entries are not password-protected

然后我看了this和this,所以我删除了entry元素(我相信是私钥的东西),就变成了:

<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
    <constructor-arg value="classpath:security/samlKeystore.jks"/>
    <constructor-arg type="java.lang.String" value="123456"/>
    <constructor-arg>
        <map/>
    </constructor-arg>
    <constructor-arg type="java.lang.String" value="identtrustca"/>
</bean>

现在问题变成了:

Servlet.service() for servlet [jsp] in context with path [/spring-security-saml2-sample] threw exception
java.lang.RuntimeException: Key with alias identtrustca doesn't have a private key

第 2 天第 2 部分: 现在我明白了,我应该只使用旧的 samlKeystore.jks,而不是创建新的密钥库。如果使用keytool -list -V -keystore d:\temp\samlKeystore.jks 可以看到它包含 3 个项目(原来是 2 个)。

我不明白为什么 SP 从 IdP 元数据中获取公钥,但仍需要 IdP 的 ca.cer?我相信来自 auth 网站的 ca.cer 是 IdP 的证书,虽然它与我通过openssl s_client -connect idp.ssocircle.com:443 -showcerts 获得的证书不同

此外,现在我得到的是Error decoding incoming SAML message,而不是HTTP Status 401 - Authentication Failed: Response issue time is either too old or with date in the future

第 3 天 当我启动 SP,从 chrome 和 firefox 访问 http://localhost:8080/spring-security-saml2-sample/ 时,当我在一个资源管理器中按下全局注销并刷新另一个资源管理器时,它不会显示注销页面。我认为对于 SSO,来自同一个 SP 的全局注销应该忽略多会话,例如,一旦我在一个“登录”中全局注销,我应该在另一个“登录”中自动注销,我应该看到一个注销页面另一个资源管理器(如果我刷新)。实际上,对于多个SP,一个我注销一个,我应该能够看到我曾经登录的所有SP的注销。

第 n 天 通过更改 saml 项目 url,需要修改 idp.ssocircle.com 中的元数据(管理元数据)。否则,idp 网站会显示错误。这是写在 docs/reference/htmlsingle/index.html

【问题讨论】:

见***.com/questions/40991685/… @VladimírSchäfer 嗨,我更新了问题,第 2 天部分。请看一下谢谢。 @VladimírSchäfer 请参阅第 2 天第 2 部分,第 2 天已清除。 :) 【参考方案1】:

IDP 元数据中的证书用于对 SAML XML 文档进行数字签名。您的失败来自的证书来自 www.ssocircle.com 的 HTTPS 端点,您的 Spring SAML 在工件解析期间调用该端点。这两个证书通常不同。

SSOCircle 最近更改了为 www.ssocircle.com 颁发证书的证书颁发机构,SAML ERROR: PKIX path construction failed for untrusted credential 中描述的操作是让该 CA 受信任。

您最近的错误可能是由于您的 Spring SAML 机器上的时间没有正确同步。

【讨论】:

检查响应消息,我注意到 ssocircle 是未来 3 分钟。所以按照***.com/questions/25647925/…解决这个问题 默认情况下,Spring SAML 将有关用户会话的信息存储在 HTTP 会话(= cookie)中,SP 端的全局注销只会使浏览器有权访问的会话无效。 Spring SAML 的 LogoutProcessing 支持添加自定义 LogoutHandler,因此如果您的应用程序确实有一个包含应用程序范围的用户会话的存储,您可以在那里使所有会话无效。

以上是关于HTTP 状态 401 - 身份验证失败:解码传入 SAML 消息时出错的主要内容,如果未能解决你的问题,请参考以下文章

CAS HTTP 401 - 身份验证失败:凭据错误

如何显示 HTTP 401 基本身份验证对话框

关于身份验证问题的 Swift 状态码 401

通过基本身份验证访问 REST 的 Angular2 抛出“预检响应具有无效的 HTTP 状态代码 401”

不使用 HTTP 基本身份验证时 HTTP 401 未经授权?

在身份验证失败时返回自定义状态码 .Net Core