You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

反向代理下Spring Security 6 SAML2 SP认证异常排查求助

问题:反向代理下Spring Security 6 SAML2 SP解密失败,提示无依赖方注册

我用OpenSAML结合Spring Security 6搭建SAML2服务提供商(SP),本地运行完全正常,但部署到反向代理后,SAML响应无法正确处理,抛出如下异常:

mes:tc:SAML:2.0:assertion}EncryptedAssertion │
with propagation set to true                                                                                                                                                                          │
│2024-10-27 11:46:04.302000[http-nio-5003-exec-175] TRACE o.o.core.xml.AbstractXMLObject - Releasing cached DOM reprsentation for {urn:oasis:names:tc:SAML:2.0:protocol}Response                      ┤
│2024-10-27 11:46:04.302000[http-nio-5003-exec-175] TRACE o.o.core.xml.AbstractXMLObject - Releasing cached DOM reprsentation for parent of {urn:oasis:names:tc:SAML:2.0:protocol}Response with propag│
ation set to true                                                                                                                                                                                     ┤
│2024-10-27 11:46:04.302000[http-nio-5003-exec-175] TRACE o.s.s.s.p.s.w.a.Saml2WebSsoAuthenticationFilter - Failed to process authentication request                                                  ┤
│org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException: No relying party registration found                                                                 ┤
│       at org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter.attemptAuthentication(Saml2WebSsoAuthenticationFilter.java:127)                     ┤
│       at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:231)                                           ┤
│       at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:221)                                           ┤
│       at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)                                                                                   ┤
│       at org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter.doFilterInternal(Saml2WebSsoAuthenticationRequestFilter.java:100)                           ┤
│       at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)                                                                                                ┤
│       at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)                                                                                   ┤
│       at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:107)                                                                                        ┤
│       at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:93)                                                                                         ┤
│       at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)                                                                                   ┤
│       at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)                                                                                      │
│       at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)                                                                                    │
│       at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)                                                                                                ┤
│       at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)                                                                                   │
│       at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:82)                                                                         ┤
│       at org.springframework.security.web.context.SecurityContextHolderFilter.doFilter(SecurityContextHolderFilter.java:69)                                                                         ┤
│       at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374)                                                                                   │
│       at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:62)                                         ┤
│       at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)

流量路径为:https://my.public.domain/ → http://internal/(SAML2 SP部署在internal节点)。请问:

  1. 是否需要在SP中处理X-Forwarded-Proto这类HTTPS相关头信息?
  2. 反向代理的证书是否会干扰通信?

SP已配置本地自签名证书用于签名和解密,核心代码如下:

RSAPrivateKey k = RsaKeyConverters.pkcs8().convert(new FileInputStream(privateKeyResource));

log.info("certificate path:"+certificateResource.getAbsolutePath());
log.info("private key path:"+privateKeyResource.getAbsolutePath());

X509Certificate certificate = X509Support.decodeCertificate(certificateResource);
Saml2X509Credential credential = Saml2X509Credential.signing(k, certificate);
Saml2X509Credential decryptCredential = Saml2X509Credential.decryption(k, certificate);

RelyingPartyRegistration registration = RelyingPartyRegistrations
        .fromMetadataLocation("classpath:saml/metadata.xml").registrationId(relyingPartyEntityId)
        .authnRequestsSigned(true)
        .signingX509Credentials(c -> c.add(credential))
        .decryptionX509Credentials(c -> c.add(decryptCredential))
        .assertingPartyDetails(party -> party
                        .entityId(relyingPartyEntityId)
        ).assertionConsumerServiceLocation(virtuACSLocation)
        .entityId(relyingPartyEntityId)
        .build();

解答

1. 必须处理X-Forwarded-Proto这类转发头

异常核心是No relying party registration found,并非直接的解密失败——Spring Security在处理SAML响应时,会校验请求的ACS(断言消费者服务)地址与依赖方注册中配置的地址是否一致。

反向代理后,SP内部收到的请求协议是HTTP,但IDP那边配置的ACS地址是HTTPS的公网地址(https://my.public.domain/...),协议不匹配会导致Spring无法找到对应的依赖方注册,进而抛出异常。

解决方法:

  • 在Spring Boot配置文件中添加:
    server.forward-headers-strategy=NATIVE
    
    让服务器自动识别反向代理传递的X-Forwarded-Proto、X-Forwarded-Host等头,还原真实的请求地址。
  • 或者在SecurityFilterChain中显式配置ForwardedHeaderFilter,确保Spring能正确解析真实请求信息。

2. 反向代理证书不会干扰SAML通信

SAML的加密、签名操作是SP与IDP之间的端到端流程,使用的是你配置的自签名SAML证书。反向代理的证书仅用于客户端(用户浏览器)与反向代理之间的HTTPS通信,和SP与IDP的SAML加密逻辑完全无关。

但需要确保反向代理不篡改SAML响应的内容和请求头,否则会导致签名验证失败或XML解析错误。


额外排查点

  • 确认IDP元数据中配置的ACS地址,与你代码中virtuACSLocation的值完全一致(必须是https://my.public.domain/...)。
  • 检查反向代理是否正确传递了X-Forwarded-Proto: https、X-Forwarded-Host: my.public.domain头。
  • 开启Spring Security的DEBUG日志,查看依赖方匹配时的URL对比细节,确认地址/协议不匹配的具体原因。

内容的提问来源于stack exchange,提问作者knocker_d

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 22:47:05