Spring Security SAML2 SP登出响应未发送及会话失效问题
Spring Security SAML2 SP 6.3.3 IDP发起登出异常问题
问题场景
使用spring-security-saml2-service-provider 6.3.3实现SAML2服务提供商,登录功能正常,但IDP发起登出时出现以下问题:
- SP能接收并处理登出请求,日志无报错
- 调试可见
saml2LogoutRequestFilter已生成Saml2LogoutResponse,但响应未发送至IDP - 若将
Saml2MessageBinding.POST改为Saml2MessageBinding.GET:- 登出响应可正常送达IDP
- 但SP本地会话未失效,仍能获取已认证用户信息
安全配置代码
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(authz -> authz .requestMatchers(new AntPathRequestMatcher("/samlLogin")).authenticated() .requestMatchers(new AntPathRequestMatcher("/*")).permitAll() ) .saml2Login(Customizer.withDefaults()) .saml2Metadata(Customizer.withDefaults()) .saml2Logout(Customizer.withDefaults()) .securityContext(securityContext -> securityContext .securityContextRepository(new HttpSessionSecurityContextRepository()) ) .build(); } @Bean public RelyingPartyRegistrationRepository registrationRepository() { RelyingPartyRegistration registration = RelyingPartyRegistrations .fromMetadataLocation("idp.xml") .registrationId(registrationId) .entityId(spEntityID) .nameIdFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:transient") .singleLogoutServiceBinding(Saml2MessageBinding.POST) .singleLogoutServiceLocation(LogoutServiceLocation) .signingX509Credentials(credential -> credential.add(asSigningCredential())) .decryptionX509Credentials(decryptioncredential -> decryptioncredential.add(asDecryptionCredential())) .build(); return new InMemoryRelyingPartyRegistrationRepository(registration); }
关键日志信息
2024-11-08 18:03:45 DEBUG o.a.x.s.utils.DigesterOutputStream - Pre-digested input: 2024-11-08 18:03:45 DEBUG o.a.x.s.utils.DigesterOutputStream - <saml2p:LogoutResponse xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="https:idp.com/idp/samlSLO" ID="LR71aaf86c-46e9-4e74-b713-1df71f15c2da" InResponseTo="_1c7b9b6475e13434fff5c2e8a2286878" IssueInstant="2024-11-08T12:33:45.888Z" Version="2.0"><saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://sp.com:443</saml2:Issuer>saml2p:Status<saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"></saml2p:StatusCode></saml2p:Status></saml2p:LogoutResponse> 2024-11-08 18:03:45 DEBUG o.a.x.s.utils.SignerOutputStream - Canonicalized SignedInfo: 2024-11-08 18:03:45 DEBUG o.a.x.s.utils.SignerOutputStream - <ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:CanonicalizationMethod> <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></ds:SignatureMethod> <ds:Reference URI="#LR71aaf86c-46e9-4e74-b713-1df71f15c2da"> ds:Transforms <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform> <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:Transform> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></ds:DigestMethod> ds:DigestValue2t7PoCNN4cnijQH54VmV62w2eEryZ7BghBblG91uCII=</ds:DigestValue> </ds:Reference> </ds:SignedInfo>
解决方案
1. 修复POST绑定的响应发送问题
POST绑定的LogoutResponse需要通过自动提交表单发送到IDP,默认配置可能未正确触发渲染逻辑。自定义Saml2LogoutResponseHandler手动生成并输出表单:
.saml2Logout(logout -> logout .logoutResponseHandler((response, request, authentication) -> { Saml2LogoutResponse saml2Response = response.getSaml2Response(); RelyingPartyRegistration registration = response.getRelyingPartyRegistration(); // 生成POST绑定的自动提交表单 String form = String.format(""" <html> <body onload="document.forms[0].submit()"> <form action="%s" method="post"> <input type="hidden" name="SAMLResponse" value="%s"/> <input type="hidden" name="RelayState" value="%s"/> </form> </body> </html> """, registration.getSingleLogoutServiceLocation(), Base64.getEncoder().encodeToString(saml2Response.getBytes(StandardCharsets.UTF_8)), request.getRequest().getParameter("RelayState") ); HttpServletResponse servletResponse = request.getServletResponse(); servletResponse.setContentType("text/html;charset=UTF-8"); servletResponse.getWriter().write(form); }) )
2. 修复GET绑定时的会话失效问题
GET绑定模式下,需手动添加会话清理的LogoutHandler:
.saml2Logout(logout -> logout .addLogoutHandler(new SecurityContextLogoutHandler()) .logoutSuccessHandler((request, response, authentication) -> { // 清理会话 request.getSession().invalidate(); // 可选:跳转到首页或登出成功页面 response.sendRedirect("/"); }) )
3. 修正IDP地址配置错误
从日志可见Destination字段为https:idp.com/idp/samlSLO,缺少双斜杠,需检查LogoutServiceLocation配置,确保为完整的HTTPS地址:https://idp.com/idp/samlSLO。
内容的提问来源于stack exchange,提问作者Sasirekha Kumaran
相关产品推荐
相关产品推荐

