From b24e38469b049ff2e521739f4b1e302a579529de Mon Sep 17 00:00:00 2001 From: Andrey Litvitski Date: Mon, 11 Aug 2025 16:49:39 +0300 Subject: [PATCH] Prevent caching of non-document requests in HttpSessionRequestCache When the browser requests a missing favicon.ico on the login page, it triggers an ERROR dispatch. This request was mistakenly saved and caused redirection to /error after login instead of the original URL. This change checks for DispatcherType.ERROR and Sec-Fetch-Dest header to avoid caching such requests. Closes #17686 Signed-off-by: Andrey Litvitski --- .../web/savedrequest/HttpSessionRequestCache.java | 13 +++++++++++++ .../savedrequest/HttpSessionRequestCacheTests.java | 14 ++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/web/src/main/java/org/springframework/security/web/savedrequest/HttpSessionRequestCache.java b/web/src/main/java/org/springframework/security/web/savedrequest/HttpSessionRequestCache.java index 9e51943f72..e1c8a205cb 100644 --- a/web/src/main/java/org/springframework/security/web/savedrequest/HttpSessionRequestCache.java +++ b/web/src/main/java/org/springframework/security/web/savedrequest/HttpSessionRequestCache.java @@ -16,6 +16,7 @@ package org.springframework.security.web.savedrequest; +import jakarta.servlet.DispatcherType; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; @@ -38,6 +39,7 @@ * * @author Luke Taylor * @author Eddú Meléndez + * @author Andrey Litvitski * @since 3.0 */ public class HttpSessionRequestCache implements RequestCache { @@ -61,6 +63,17 @@ public class HttpSessionRequestCache implements RequestCache { */ @Override public void saveRequest(HttpServletRequest request, HttpServletResponse response) { + boolean documentRequest = "document".equals(request.getHeader("Sec-Fetch-Dest")); + boolean dispatchError = DispatcherType.ERROR.equals(request.getDispatcherType()); + + if (!documentRequest && dispatchError) { + if (this.logger.isTraceEnabled()) { + this.logger + .trace("Did not save request because it is an ERROR dispatcher and not a primary document request"); + } + return; + } + if (!this.requestMatcher.matches(request)) { if (this.logger.isTraceEnabled()) { this.logger diff --git a/web/src/test/java/org/springframework/security/web/savedrequest/HttpSessionRequestCacheTests.java b/web/src/test/java/org/springframework/security/web/savedrequest/HttpSessionRequestCacheTests.java index dc05a00562..7ad824810f 100644 --- a/web/src/test/java/org/springframework/security/web/savedrequest/HttpSessionRequestCacheTests.java +++ b/web/src/test/java/org/springframework/security/web/savedrequest/HttpSessionRequestCacheTests.java @@ -21,6 +21,7 @@ import java.util.Locale; import java.util.Map; +import jakarta.servlet.DispatcherType; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -40,6 +41,7 @@ /** * @author Luke Taylor * @author Eddú Meléndez + * @author Andrey Litvitski * @since 3.0 */ public class HttpSessionRequestCacheTests { @@ -168,6 +170,18 @@ public void getMatchingRequestWhenMatchingRequestParameterNameSetThenDoesNotInvo verify(request, never()).getParameterMap(); } + // gh-17686 + @Test + public void saveRequestShouldNotSaveRequestWhenErrorDispatcherAndNonDocumentRequest() { + HttpSessionRequestCache cache = new HttpSessionRequestCache(); + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/destination"); + request.setDispatcherType(DispatcherType.ERROR); + request.addHeader("Sec-Fetch-Dest", "image"); + MockHttpServletResponse response = new MockHttpServletResponse(); + cache.saveRequest(request, response); + assertThat(request.getSession().getAttribute(HttpSessionRequestCache.SAVED_REQUEST)).isNull(); + } + private static final class CustomSavedRequest implements SavedRequest { private final SavedRequest delegate;