From 0010fca7958a444656c9193a6be63a0f238fc52d Mon Sep 17 00:00:00 2001 From: Develop-KIM Date: Thu, 9 Jul 2026 20:03:19 +0900 Subject: [PATCH] Use configured logout matcher for SAML logout Signed-off-by: Develop-KIM --- .../web/configurers/LogoutConfigurer.java | 9 +++- .../saml2/Saml2LogoutConfigurer.java | 6 ++- .../saml2/Saml2LogoutConfigurerTests.java | 42 +++++++++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.java index f4e9cf6e9f8..73a02b0f269 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/LogoutConfigurer.java @@ -349,7 +349,14 @@ private SecurityContextRepository getSecurityContextRepository(H http) { return securityContextRepository; } - private RequestMatcher getLogoutRequestMatcher(H http) { + /** + * Gets the {@link RequestMatcher} that will be used to determine if logout should + * occur. + * @param http the builder to use + * @return the {@link RequestMatcher} that will be used to determine if logout + * should occur + */ + public RequestMatcher getLogoutRequestMatcher(H http) { if (this.logoutRequestMatcher != null) { return this.logoutRequestMatcher; } diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurer.java index 1c519e786cd..53c266db17b 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurer.java @@ -123,6 +123,8 @@ public final class Saml2LogoutConfigurer> private LogoutSuccessHandler logoutSuccessHandler; + private RequestMatcher logoutRequestMatcher; + private LogoutRequestConfigurer logoutRequestConfigurer; private LogoutResponseConfigurer logoutResponseConfigurer; @@ -204,6 +206,7 @@ public void configure(H http) { if (logout != null) { this.logoutHandlers = logout.getLogoutHandlers(); this.logoutSuccessHandler = logout.getLogoutSuccessHandler(); + this.logoutRequestMatcher = logout.getLogoutRequestMatcher(http); } RelyingPartyRegistrationRepository registrations = getRelyingPartyRegistrationRepository(http); http.addFilterBefore(createLogoutRequestProcessingFilter(registrations), CsrfFilter.class); @@ -271,7 +274,8 @@ private Saml2RelyingPartyInitiatedLogoutFilter createRelyingPartyLogoutFilter( } private RequestMatcher createLogoutMatcher() { - RequestMatcher logout = getRequestMatcherBuilder().matcher(HttpMethod.POST, this.logoutUrl); + RequestMatcher logout = (this.logoutRequestMatcher != null) ? this.logoutRequestMatcher + : getRequestMatcherBuilder().matcher(HttpMethod.POST, this.logoutUrl); RequestMatcher saml2 = new Saml2RequestMatcher(getSecurityContextHolderStrategy()); return new AndRequestMatcher(logout, saml2); } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurerTests.java index 11450950a14..605e4464bf9 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurerTests.java @@ -197,6 +197,19 @@ public void saml2LogoutWhenDefaultsThenLogsOutAndSendsLogoutRequest() throws Exc verify(logoutHandler).logout(any(), any(), any()); } + // gh-10821 + @Test + public void saml2LogoutWhenLogoutRequestMatcherConfiguredThenUses() throws Exception { + this.spring.register(Saml2LogoutRequestMatcherConfig.class).autowire(); + MvcResult result = this.mvc.perform(post("/signout").with(authentication(this.user)).with(csrf())) + .andExpect(status().isFound()) + .andReturn(); + String location = result.getResponse().getHeader("Location"); + LogoutHandler logoutHandler = this.spring.getContext().getBean(LogoutHandler.class); + assertThat(location).startsWith("https://ap.example.org/logout/saml2/request"); + verify(logoutHandler).logout(any(), any(), any()); + } + // gh-19128 @Test public void saml2LogoutWhenBuilderBeanWithBasePathThenLogoutUrlIgnoresBasePath() throws Exception { @@ -644,6 +657,35 @@ LogoutSuccessHandler logoutSuccessHandler() { } + @Configuration + @EnableWebSecurity + @Import(Saml2LoginConfigBeans.class) + static class Saml2LogoutRequestMatcherConfig { + + LogoutHandler mockLogoutHandler = mock(LogoutHandler.class); + + @Bean + SecurityFilterChain web(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()) + .logout((logout) -> logout + .logoutRequestMatcher(pathPattern(HttpMethod.POST, "/signout")) + .addLogoutHandler(this.mockLogoutHandler) + ) + .saml2Login(withDefaults()) + .saml2Logout(withDefaults()); + return http.build(); + // @formatter:on + } + + @Bean + LogoutHandler logoutHandler() { + return this.mockLogoutHandler; + } + + } + @Configuration @EnableWebSecurity @Import(Saml2LoginConfigBeans.class)