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

Spring Security SAML扩展对接Azure AD:单点登出(SLO)异常问题

问题:Spring Security SAML 1.0.10对接Azure AD跨应用单点登出失败

我使用Spring Security SAML扩展1.0.10版本将两个Web应用配置为SAML服务提供商(SP),身份提供商(IdP)为Azure AD。跨应用单点登录(SSO)功能正常,但单点登出(SLO)存在问题:当在两个浏览器标签页分别登录两个应用后,从其中一个应用发起登出,该应用可正常登出,但另一个应用的登出失败。

启用调试日志后,第二个应用的关键日志输出如下:

DEBUG [org.springframework.security.web.FilterChainProxy] - Securing GET /saml/SingleLogout?SAMLRequest=lZJfb5swFMW%2fCuLdYGPzxxZBypYtQ20aqVlbaS%2bRgUuKBjbFJs3HLwT1IdJUaW%2f2lY9%2b51yf1Miu7cW9PunRPsLbCMY6%2bWblHmscFoREGHHGJGJJTBHnCUYhl1SWUU1rWrrOMwym0WrlBh52ndyYEXJlrFR2GuEgQAQjQn8TKjATAfcinvxxnc1EaZS0V%2bWrtb0Rvt%2fKcYDW6wcwMJybUnql9sa%2f%2ftN03UklT9CBsv5s2D806tTC4tp1HrTdq%2f2wri0Mt1h2g710rTLimnjljoMSWprGCCU7MMKW4rDe3YspiOgHbXWpWzdLr5GGRfq1SJrJ6BzJzT4jGWu890ZV%2bt14CqwfSYkZqUrEo4IiFsoKceAFimKQAS4JgwT81F%2bYWfowMfKN81MPnbRfw%2bdJU6H6%2blT0868YO63L%2fU%2fnYfwcbrvzcWe%2f3R2L%2fO7Hmv%2b6BPtX%2fXLenrakfvveRo8o3B4anfqLvyxdOnQAM1chVxVcsiMNaREDo6jmkiBWQ4SSuJYowFUZTgcSYJz6%2f1B%2bDm8qmX0A&Signature=H2iMTbizxEM8ooIUfV%2fyZ8zZfkK8J9CXbVako2sPk9EUw1xjRUXfaCUbO3gpsjKvUD61UHEbpOexnMhCtqCJnItC81hIVp9dI2%2bSGqJ3%2fIYFtxDMqVAsP%2fxsEZpL%2f15OkZ0rj0n1nAiU7dT3xC0K5TDtjUWciKqbt0MkJgvKyvkZyjZGjhclfTGo4AJQrEkBVxkw8%2b2Evwgmxpk0taOfhq9sHaiRLzvVAxhCse9GZhbQehxyxtWWNDYt8Ks7JJJR5UDfdszX4E5J2576seSxvop3EeJLlULjjNm3FJZcXoKKAXzA%2bfHqYBYFD9rIyuEXaJsFEC0p8SSAHwKgDOq3PA%3d%3d&SigAlg=http%3a%2f%2fwww.w3.org%2f2001%2f04%2fxmldsig-more%23rsa-sha256
DEBUG [org.springframework.security.web.context.SecurityContextPersistenceFilter] - Set SecurityContextHolder to empty SecurityContext
...
DEBUG [org.springframework.security.saml.SAMLLogoutProcessingFilter] - Received logout request is invalid, responding with error
org.springframework.security.saml.SAMLStatusException: No user is logged in
    at org.springframework.security.saml.websso.SingleLogoutProfileImpl.processLogoutRequest(SingleLogoutProfileImpl.java:175) ~[spring-security-saml2-core-1.0.10.RELEASE.jar:1.0.10.RELEASE]
    at org.springframework.security.saml.SAMLLogoutProcessingFilter.processLogout(SAMLLogoutProcessingFilter.java:181) [spring-security-saml2-core-1.0.10.RELEASE.jar:1.0.10.RELEASE]
    ...

Azure AD中已为app2配置Front-channel logout URL为https://server.com/app2/saml/SingleLogout,端点已被调用且使用HTTP-Redirect绑定,但在SAMLLogoutProcessingFilter中,SecurityContextHolder.getContext().getAuthentication()返回null,导致因无用户会话而登出失败。


核心原因分析

Azure AD的前端通道登出通过浏览器重定向到每个SP的登出端点实现,但此时第二个应用的请求可能未携带有效会话Cookie,或Cookie未被Spring Security正确识别,导致无法找到用户认证上下文。

服务提供商(SP)端配置调整

  • 调整会话Cookie属性:
    如果两个应用部署在同一主域名下(如server.com/app1和server.com/app2),将会话Cookie的SameSite设置为Lax或None(跨域场景),同时开启Secure属性(HTTPS环境),确保浏览器在重定向请求时携带Cookie。
    Spring Boot中可通过配置项设置:

    server.servlet.session.cookie.same-site=Lax
    server.servlet.session.cookie.secure=true
    
  • 允许登出端点匿名访问:
    确保/saml/SingleLogout端点被排除在Spring Security的认证拦截之外,避免过滤器提前清空SecurityContext:

    http.authorizeRequests()
        .antMatchers("/saml/**").permitAll()
        .anyRequest().authenticated();
    
  • 自定义登出过滤器处理无会话场景:
    默认的SingleLogoutProfileImpl会在无认证信息时抛出异常,可自定义过滤器,即使无用户会话也返回成功响应给Azure AD:

    public class CustomSAMLLogoutProcessingFilter extends SAMLLogoutProcessingFilter {
    
        public CustomSAMLLogoutProcessingFilter(SAMLContextProvider contextProvider, SAMLLogoutSuccessHandler successHandler, SAMLLogoutFailureHandler failureHandler) {
            super(contextProvider, successHandler, failureHandler);
        }
    
        @Override
        protected void processLogout(HttpServletRequest request, HttpServletResponse response, SAMLMessageContext context) throws ServletException, IOException {
            try {
                super.processLogout(request, response, context);
            } catch (SAMLStatusException e) {
                if ("No user is logged in".equals(e.getMessage())) {
                    // 无会话时直接返回成功响应
                    getSuccessHandler().onLogoutSuccess(request, response, null);
                } else {
                    getFailureHandler().onLogoutFailure(request, response, e);
                }
            }
        }
    }
    
  • 关联NameID与会话:
    SSO成功后将用户的NameID存储到会话中,处理登出请求时手动匹配,即使SecurityContext为空也可清空会话:

    // 认证成功时存储NameID
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    if (auth instanceof SAMLAuthenticationToken) {
        SAMLAuthenticationToken samlToken = (SAMLAuthenticationToken) auth;
        request.getSession().setAttribute("SAML_NAME_ID", samlToken.getNameID());
    }
    

身份提供商(IdP)端配置调整

  • 确认Front-channel logout配置:
    在Azure AD应用注册中,确保每个SP的Front-channel logout URL正确设置为/saml/SingleLogout端点,且已开启Front-channel logout功能。
  • 验证LogoutRequest格式:
    使用SAML tracer工具捕获Azure AD发送的LogoutRequest,确认其中的NameID和SessionIndex与SSO时SP收到的一致,确保Spring Security能关联到用户会话。

调试建议

  • 用浏览器开发者工具检查登出请求中是否携带SP的会话Cookie(如JSESSIONID),若未携带则说明Cookie配置存在问题。
  • 跟踪SecurityContextPersistenceFilter的日志,确认SecurityContext被清空的时机。

内容的提问来源于stack exchange,提问作者A-G

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 16:55:36