This is an automated email from the ASF dual-hosted git repository.

lprimak pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/shiro.git


The following commit(s) were added to refs/heads/main by this push:
     new fd1b1a528 Bugfix: improve form resubmit handling  (#2858)
fd1b1a528 is described below

commit fd1b1a528565045f7c428e66085bfffbe4b70392
Author: Lenny Primak <[email protected]>
AuthorDate: Sun Aug 16 12:45:24 2026 -0500

    Bugfix: improve form resubmit handling  (#2858)
---
 src/owasp-suppression.xml                          |  17 +-
 .../shiro/ee/filters/FormResubmitSupport.java      | 202 +++++++++++++++------
 .../ee/filters/FormResubmitSupportCookies.java     |  32 ++++
 .../shiro/ee/filters/FormResubmitValidator.java    |  30 +--
 .../org/apache/shiro/ee/filters/ShiroFilter.java   |   4 +-
 .../shiro/web/filter/authc/NoAccessFilter.java     |   2 +-
 .../web/servlet/ShiroHttpServletResponse.java      |  30 +--
 7 files changed, 215 insertions(+), 102 deletions(-)

diff --git a/src/owasp-suppression.xml b/src/owasp-suppression.xml
index 03ae97d0c..6b0ed1cab 100644
--- a/src/owasp-suppression.xml
+++ b/src/owasp-suppression.xml
@@ -19,16 +19,11 @@
   -->
 <suppressions 
xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.1.xsd";>
 
-    <suppress>
-        <notes><![CDATA[ file name: tomcat-embed-core-8.5.5.jar ]]></notes>
-        <sha1>d55e12a418ff99ecd723a118c2a28bb91079972d</sha1>
-        <cpe>cpe:/a:apache:tomcat:8.5.5</cpe>
-    </suppress>
-
-    <suppress>
-        <notes><![CDATA[ file name: tomcat-embed-websocket-8.5.5.jar 
]]></notes>
-        <sha1>fd99cd1cd4c824abdf03466f0509f067747f0d1a</sha1>
-        <cpe>cpe:/a:apache:tomcat:8.5.5</cpe>
-    </suppress>
+<!--    EXAMPLE-->
+<!--    <suppress>-->
+<!--        <notes><![CDATA[ file name: tomcat-embed-core-8.5.5.jar 
]]></notes>-->
+<!--        <sha1>d55e12a418ff99ecd723a118c2a28bb91079972d</sha1>-->
+<!--        <cpe>cpe:/a:apache:tomcat:8.5.5</cpe>-->
+<!--    </suppress>-->
 
 </suppressions>
diff --git 
a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitSupport.java
 
b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitSupport.java
index 1aa637d80..44f1cf56d 100644
--- 
a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitSupport.java
+++ 
b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitSupport.java
@@ -39,11 +39,11 @@ import java.time.Duration;
 import java.util.Collections;
 import org.apache.shiro.crypto.CryptoException;
 import org.apache.shiro.ee.filters.Forms.FallbackPredicate;
+import static 
org.apache.shiro.ee.filters.FormResubmitSupportCookies.initializeCookies;
 import static 
org.apache.shiro.ee.filters.FormResubmitSupportCookies.transformCookieHeader;
 import static 
org.apache.shiro.ee.listeners.EnvironmentLoaderListener.isFormResubmitDisabled;
 import java.io.IOException;
 import java.net.CookieManager;
-import java.net.HttpCookie;
 import java.net.URI;
 import java.net.URLDecoder;
 import java.net.http.HttpClient;
@@ -51,18 +51,24 @@ import java.net.http.HttpHeaders;
 import java.net.http.HttpRequest;
 import java.net.http.HttpResponse;
 import java.nio.charset.StandardCharsets;
+import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.UUID;
 import static java.util.function.Predicate.not;
 import static org.apache.shiro.ee.listeners.IniEnvironment.hasFacesContext;
 import static 
org.apache.shiro.web.filter.authc.NoAccessFilter.FORM_RESUBMIT_CHECK_SERVLET_PATH;
-import static 
org.apache.shiro.web.mgt.CookieRememberMeManager.DEFAULT_REMEMBER_ME_COOKIE_NAME;
+import static org.apache.shiro.web.filter.authz.PortFilter.DEFAULT_HTTP_PORT;
+import static org.apache.shiro.web.filter.authz.PortFilter.HTTP_SCHEME;
+import static org.apache.shiro.web.filter.authz.SslFilter.DEFAULT_HTTPS_PORT;
+import static org.apache.shiro.web.filter.authz.SslFilter.HTTPS_SCHEME;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import jakarta.servlet.ServletContext;
 import jakarta.servlet.ServletRequest;
-import jakarta.servlet.http.Cookie;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.AccessLevel;
@@ -73,7 +79,6 @@ import lombok.RequiredArgsConstructor;
 import lombok.SneakyThrows;
 import lombok.ToString;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.cache.Cache;
 import org.apache.shiro.lang.codec.Base64;
 import org.apache.shiro.mgt.AbstractRememberMeManager;
@@ -86,6 +91,7 @@ import org.jsoup.Jsoup;
 import org.jsoup.select.Elements;
 import org.omnifaces.util.Faces;
 import org.omnifaces.util.Servlets;
+import org.owasp.encoder.Encode;
 
 /**
  * supporting methods for {@link Forms}
@@ -99,20 +105,20 @@ public class FormResubmitSupport {
     static final String FORM_IS_RESUBMITTED = 
"org.apache.shiro.form-is-resubmitted";
     static final String FORM_RESUBMIT_WHITELIST = 
"org.apache.shiro.form-resubmit-whitelist";
     static final String FORM_RESUBMIT_BLACKLIST = 
"org.apache.shiro.form-resubmit-blacklist";
+    static final String FORM_DATA_CACHE = "org.apache.shiro.form-data-cache";
     // encoded view state
     private static final String FACES_VIEW_STATE = "jakarta.faces.ViewState";
     private static final String FACES_VIEW_STATE_EQUALS = FACES_VIEW_STATE + 
"=";
     private static final Pattern VIEW_STATE_PATTERN
-            = 
Pattern.compile(String.format("(.*)(%s[-]?[\\d]+:[-]?[\\d]+)(.*)", 
FACES_VIEW_STATE_EQUALS));
+            = Pattern.compile(String.format("(.*)(%s-?\\d+:-?\\d+)(.*)", 
FACES_VIEW_STATE_EQUALS));
     private static final String FACES_SOURCE = "jakarta.faces.source";
     private static final String FACES_SOURCE_EQUALS = FACES_SOURCE + "=";
     static final Pattern FACES_SOURCE_PATTERN
-            = Pattern.compile(String.format("[\\&]?%s([\\w\\s:%%\\d]*)(.*)", 
FACES_SOURCE_EQUALS));
+            = Pattern.compile(String.format("&?%s([\\w\\s:%%d]*)(.*)", 
FACES_SOURCE_EQUALS));
     private static final Pattern PARTIAL_REQUEST_PATTERN
-            = 
Pattern.compile("[\\&]?(%s.\\w+|%s.\\w+|%s)=[\\w\\s:%%\\d]*".formatted(
+            = Pattern.compile("&?(%s.\\w+|%s.\\w+|%s)=[\\w\\s:%%d]*".formatted(
             "jakarta.faces.partial", "jakarta.faces.behavior", FACES_SOURCE));
-    private static final Pattern INITIAL_AMPERSAND = Pattern.compile("^\\&");
-    private static final String FORM_DATA_CACHE = 
"org.apache.shiro.form-data-cache";
+    private static final Pattern INITIAL_AMPERSAND = Pattern.compile("^&");
     private static final String FORM_RESUBMIT_HOST = 
"org.apache.shiro.form-resubmit-host";
     private static final String FORM_RESUBMIT_PORT = 
"org.apache.shiro.form-resubmit-port";
     private static final Optional<String> RESUBMIT_HOST = 
Optional.ofNullable(System.getProperty(FORM_RESUBMIT_HOST));
@@ -129,6 +135,17 @@ public class FormResubmitSupport {
     private static final Optional<Long> RESUBMIT_BLACK_LIST_TTL_SECONDS =
             
Optional.ofNullable(System.getProperty(FORM_RESUBMIT_BLACK_LIST_TTL_SECONDS)).map(Long::valueOf);
     private static final long DEFAULT_RESUBMIT_BLACK_LIST_TTL_SECONDS = 60L;
+    private static final String SEC_FETCH_SITE = "Sec-Fetch-Site";
+    private static final String ORIGIN = "Origin";
+    private static final String CACHE_CONTROL = "Cache-Control";
+    private static final String NO_STORE = "no-store";
+    private static final String PRAGMA = "Pragma";
+    private static final String EXPIRES = "Expires";
+    private static final String NO_CACHE = "no-cache";
+    private static final Set<String> SECURITY_HEADERS =
+            Set.of("Content-Security-Policy", 
"Content-Security-Policy-Report-Only",
+                    "X-Content-Type-Options", "Referrer-Policy", 
"X-Frame-Options",
+                    "Cross-Origin-Opener-Policy", "Strict-Transport-Security");
 
     static class HttpMethod {
         static final String GET = "GET";
@@ -164,7 +181,7 @@ public class FormResubmitSupport {
 
     static void savePostDataForResubmit(HttpServletRequest request, 
HttpServletResponse response, @NonNull String loginUrl) {
         if (isPostRequest(request) && 
isSecurityManagerTypeOf(getSecurityManager(),
-                DefaultSecurityManager.class)) {
+                DefaultSecurityManager.class) && shouldSavePostData(request)) {
             String postData = getPostData(request);
             var cacheKey = UUID.randomUUID();
             DefaultSecurityManager dsm = 
getSecurityManager(DefaultSecurityManager.class);
@@ -203,22 +220,21 @@ public class FormResubmitSupport {
         return request.getReader().lines().collect(Collectors.joining());
     }
 
-    static String getSavedFormDataFromKey(@NonNull String savedFormDataKey) {
+    static String getSavedFormDataFromKey(@NonNull UUID savedFormDataKey, 
Consumer<Cache<Object, ?>> cacheConsumer) {
         String savedFormData = null;
         if (isSecurityManagerTypeOf(getSecurityManager(), 
DefaultSecurityManager.class)) {
             DefaultSecurityManager dsm = 
getSecurityManager(DefaultSecurityManager.class);
             if (dsm.getCacheManager() != null) {
                 var cache = dsm.getCacheManager().getCache(FORM_DATA_CACHE);
-                var cacheKey = UUID.fromString(savedFormDataKey);
                 var rememberMeManager = getRememberMeManager();
                 if (rememberMeManager != null && 
rememberMeManager.getCipherService() != null) {
-                    var cachedData = Optional.ofNullable((byte[]) 
cache.get(cacheKey));
+                    var cachedData = Optional.ofNullable((byte[]) 
cache.get(savedFormDataKey));
                     savedFormData = cachedData.map(encryptedData ->
                             decrypt(encryptedData, 
rememberMeManager)).orElse(savedFormData);
                 } else {
-                    savedFormData = (String) cache.get(cacheKey);
+                    savedFormData = (String) cache.get(savedFormDataKey);
                 }
-                cache.remove(cacheKey);
+                cacheConsumer.accept(cache);
             }
         }
         return savedFormData;
@@ -340,17 +356,25 @@ public class FormResubmitSupport {
     private static void doRedirectToSaved(HttpServletRequest request, 
HttpServletResponse response,
             @NonNull String savedRequest, boolean resubmit) throws 
IOException, InterruptedException {
         deleteCookie(response, request.getServletContext(), 
WebUtils.SAVED_REQUEST_KEY);
-        String savedFormDataKey = Servlets.getRequestCookie(request, 
SHIRO_FORM_DATA_KEY);
+        String savedFormDataKeyString = Servlets.getRequestCookie(request, 
SHIRO_FORM_DATA_KEY);
         boolean doRedirectAtEnd = true;
-        if (savedFormDataKey != null && resubmit) {
-            String formData = getSavedFormDataFromKey(savedFormDataKey);
-            if (formData != null) {
-                Optional.ofNullable(resubmitSavedForm(formData, savedRequest,
-                        request, response, request.getServletContext(), false, 
true))
-                        .ifPresent(path -> doFacesRedirect(request, response, 
path));
-                doRedirectAtEnd = false;
-            } else {
-                deleteCookie(response, request.getServletContext(), 
SHIRO_FORM_DATA_KEY);
+        if (savedFormDataKeyString != null && resubmit) {
+            AtomicReference<Cache<Object, ?>> cache = new AtomicReference<>();
+            UUID savedFormDataKey = UUID.fromString(savedFormDataKeyString);
+            String formData = getSavedFormDataFromKey(savedFormDataKey, 
cache::set);
+            try {
+                if (formData != null) {
+                    Optional.ofNullable(resubmitSavedForm(formData, 
savedFormDataKeyString, savedRequest,
+                                    request, response, 
request.getServletContext(), false, true))
+                            .ifPresent(path -> doFacesRedirect(request, 
response, path));
+                    doRedirectAtEnd = false;
+                } else {
+                    deleteCookie(response, request.getServletContext(), 
SHIRO_FORM_DATA_KEY);
+                }
+            } finally {
+                if (cache.get() != null) {
+                    cache.get().remove(savedFormDataKey);
+                }
             }
         }
         if (doRedirectAtEnd) {
@@ -417,7 +441,7 @@ public class FormResubmitSupport {
         return loginUrl != null && 
request.getRequestURI().equals(request.getContextPath() + loginUrl);
     }
 
-    static String resubmitSavedForm(@NonNull String savedFormData, @NonNull 
String savedRequest,
+    static String resubmitSavedForm(@NonNull String savedFormData, String 
savedFormDataKey, @NonNull String savedRequest,
             HttpServletRequest originalRequest, HttpServletResponse 
originalResponse,
             ServletContext servletContext, boolean rememberedAjaxResubmit, 
boolean redirect)
             throws InterruptedException, IOException {
@@ -429,14 +453,18 @@ public class FormResubmitSupport {
         }
         if 
(Boolean.TRUE.toString().equals(originalRequest.getHeader(FORM_IS_RESUBMITTED)))
 {
             log.debug("Form resubmit: internal auth failure");
+            setNoStoreHeaders(originalResponse);
             originalResponse.setStatus(AUTHFAIL);
             return resubmitResponseCleanup(originalRequest);
         }
         URI overriddenRequestURI = 
overrideSavedRequestURI(URI.create(savedRequest));
-        HttpClient client = buildHttpClient(overriddenRequestURI, 
servletContext, originalRequest);
-        if (!checkWhitelist(servletContext, overriddenRequestURI, client)) {
+        var cookieManager = new CookieManager();
+        HttpClient client = 
HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(2))
+                .cookieHandler(cookieManager).build();
+        if (!checkWhitelist(servletContext, overriddenRequestURI, client, 
savedFormDataKey)) {
             return savedRequest;
         }
+        initializeCookies(overriddenRequestURI, servletContext, cookieManager, 
originalRequest);
         HttpResponse<String> response;
         PartialAjaxResult decodedFormData;
         try {
@@ -535,6 +563,7 @@ public class FormResubmitSupport {
                     originalResponse.setHeader(LOCATION, 
response.headers().firstValue(LOCATION).orElseThrow());
                 }
             case OK:
+                propagateCacheHeaders(response, originalResponse);
                 // do not duplicate the session cookie(s)
                 transformCookieHeader(headers.allValues(SET_COOKIE))
                         .entrySet().stream().filter(not(entry -> entry.getKey()
@@ -546,8 +575,9 @@ public class FormResubmitSupport {
                     
originalResponse.setCharacterEncoding(StandardCharsets.UTF_8.name());
                     originalResponse.getWriter().append(String.format(
                             "<partial-response><redirect 
url=\"%s\"></redirect></partial-response>",
-                            savedRequest));
+                            Encode.forXmlAttribute(savedRequest)));
                 } else {
+                    
response.headers().firstValue(CONTENT_TYPE).ifPresent(originalResponse::setContentType);
                     originalResponse.getWriter().append(response.body());
                 }
                 return resubmitResponseCleanup(originalRequest);
@@ -564,35 +594,39 @@ public class FormResubmitSupport {
         return null;
     }
 
-    private static HttpClient buildHttpClient(URI savedRequest, ServletContext 
servletContext,
-            HttpServletRequest originalRequest) {
-        CookieManager cookieManager = new CookieManager();
-        var session = SecurityUtils.getSubject().getSession();
-        var sessionCookieName = getSessionCookieName(servletContext, 
getSecurityManager());
-        var sessionCookie = new HttpCookie(sessionCookieName, 
session.getId().toString());
-        sessionCookie.setPath(servletContext.getContextPath());
-        sessionCookie.setVersion(0);
-        cookieManager.getCookieStore().add(savedRequest, sessionCookie);
-        log.debug("Setting Cookie {}", sessionCookieName);
-        for (Cookie origCookie : originalRequest.getCookies()) {
-            if (!origCookie.getName().startsWith(sessionCookieName)
-                    && 
!origCookie.getName().equals(DEFAULT_REMEMBER_ME_COOKIE_NAME)) {
-                try {
-                    log.debug("Setting Cookie {}", origCookie.getName());
-                    HttpCookie cookie = new HttpCookie(origCookie.getName(), 
origCookie.getValue());
-                    cookie.setPath(servletContext.getContextPath());
-                    cookie.setVersion(0);
-                    cookieManager.getCookieStore().add(savedRequest, cookie);
-                } catch (IllegalArgumentException e) {
-                    log.warn("Form Resubmit: Ignoring invalid cookie [{} - 
{}]",
-                            origCookie.getName(), origCookie.getValue(), e);
-                }
-            }
+    private static void propagateCacheHeaders(HttpResponse<String> response, 
HttpServletResponse originalResponse) {
+        HttpHeaders upstreamHeaders = response.headers();
+
+        List<String> cacheControlValues = 
upstreamHeaders.allValues(CACHE_CONTROL);
+        originalResponse.setHeader(CACHE_CONTROL, cacheControlValues.isEmpty()
+                ? NO_STORE : String.join(", ", cacheControlValues));
+
+        List<String> pragmaValues = upstreamHeaders.allValues(PRAGMA);
+        originalResponse.setHeader(PRAGMA, pragmaValues.isEmpty()
+                ? NO_CACHE : String.join(", ", pragmaValues));
+
+        List<String> expiresValues = upstreamHeaders.allValues(EXPIRES);
+        if (expiresValues.isEmpty()) {
+            originalResponse.setDateHeader(EXPIRES, 0);
+        } else {
+            originalResponse.setHeader(EXPIRES, 
expiresValues.get(expiresValues.size() - 1));
         }
-        return 
HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(2)).cookieHandler(cookieManager).build();
+
+        upstreamHeaders.map().forEach((name, values) -> {
+            if (SECURITY_HEADERS.stream().anyMatch(name::equalsIgnoreCase)) {
+                values.forEach(v -> originalResponse.addHeader(name, v));
+            }
+        });
     }
 
-    private static boolean checkWhitelist(ServletContext servletContext, URI 
savedRequestURI, HttpClient client) {
+    private static void setNoStoreHeaders(HttpServletResponse response) {
+        response.setHeader(CACHE_CONTROL, NO_STORE);
+        response.setHeader(PRAGMA, NO_CACHE);
+        response.setDateHeader(EXPIRES, 0);
+    }
+
+    private static boolean checkWhitelist(ServletContext servletContext, URI 
savedRequestURI, HttpClient client,
+                                          String savedFormDataKey) {
         if (!isSecurityManagerTypeOf(getSecurityManager(), 
DefaultSecurityManager.class)) {
             log.warn("Shiro SecurityManager is not configured for form 
resubmit whitelist caching");
             return false;
@@ -612,7 +646,7 @@ public class FormResubmitSupport {
         } else if (isBlacklisted(blacklist, authority)) {
             log.debug("Form resubmit blacklist cache hit for {}", 
savedRequestURI);
             return false;
-        } else if (checkWhitelistClient(savedRequestURI, 
servletContext.getContextPath(), client)) {
+        } else if (checkWhitelistClient(savedRequestURI, 
servletContext.getContextPath(), client, savedFormDataKey)) {
             putWhitelistEntry(whitelist, authority);
             blacklist.remove(authority);
             return true;
@@ -675,7 +709,8 @@ public class FormResubmitSupport {
         return active;
     }
 
-    private static boolean checkWhitelistClient(URI savedRequestURI, String 
contextPath, HttpClient client) {
+    private static boolean checkWhitelistClient(URI savedRequestURI, String 
contextPath, HttpClient client,
+                                                String savedFormDataKey) {
         try {
             var rememberMeManager = getRememberMeManager();
             if (rememberMeManager == null || 
rememberMeManager.getCipherService() == null
@@ -687,11 +722,13 @@ public class FormResubmitSupport {
             var request = HttpRequest.newBuilder()
                     
.uri(URI.create("%s://%s%s%s".formatted(savedRequestURI.getScheme(), 
savedRequestURI.getAuthority(),
                             contextPath, FORM_RESUBMIT_CHECK_SERVLET_PATH)))
-                    .timeout(Duration.ofSeconds(3)).GET().build();
+                    .timeout(Duration.ofSeconds(3)).header(CONTENT_TYPE, 
"text/plain")
+                    
.POST(HttpRequest.BodyPublishers.ofString(rememberMeManager.getCipherService()
+                            
.encrypt(savedFormDataKey.getBytes(StandardCharsets.UTF_8),
+                                    
rememberMeManager.getEncryptionCipherKey()).toBase64())).build();
             var response = client.send(request, 
HttpResponse.BodyHandlers.ofString());
 
-            if (response.statusCode() == OK && 
Objects.equals(decrypt(response.body(), rememberMeManager),
-                    
SecurityUtils.getSubject().getSession().getId().toString())) {
+            if (response.statusCode() == OK && 
Objects.equals(decrypt(response.body(), rememberMeManager), savedFormDataKey)) {
                 log.debug("Form resubmit whitelist check succeeded for {}", 
savedRequestURI);
                 return true;
             } else {
@@ -776,4 +813,49 @@ public class FormResubmitSupport {
         return STATE_SAVING_METHOD_CLIENT.equals(
                 
servletContext.getInitParameter(STATE_SAVING_METHOD_PARAM_NAME));
     }
+
+    static boolean shouldSavePostData(HttpServletRequest request) {
+        String secFetchSite = request.getHeader(SEC_FETCH_SITE);
+        if (secFetchSite != null && !secFetchSite.isBlank()) {
+            return "same-origin".equalsIgnoreCase(secFetchSite.trim());
+        }
+
+        return originMatchesRequest(request, request.getHeader(ORIGIN));
+    }
+
+    static boolean originMatchesRequest(HttpServletRequest request, String 
originHeader) {
+        if (originHeader == null || originHeader.isBlank() || 
"null".equalsIgnoreCase(originHeader)) {
+            return false;
+        }
+
+        try {
+            URI origin = URI.create(originHeader);
+            String originScheme = origin.getScheme();
+            String originHost = origin.getHost();
+            int originPort = normalizePort(originScheme, origin.getPort());
+
+            String requestScheme = request.getScheme();
+            String requestHost = request.getServerName();
+            int requestPort = normalizePort(requestScheme, 
request.getServerPort());
+
+            return Objects.equals(originScheme, requestScheme)
+                    && Objects.equals(originHost, requestHost)
+                    && originPort == requestPort;
+        } catch (IllegalArgumentException e) {
+            return false;
+        }
+    }
+
+    private static int normalizePort(String scheme, int port) {
+        if (port >= 0) {
+            return port;
+        }
+        if (HTTPS_SCHEME.equalsIgnoreCase(scheme)) {
+            return DEFAULT_HTTPS_PORT;
+        }
+        if (HTTP_SCHEME.equalsIgnoreCase(scheme)) {
+            return DEFAULT_HTTP_PORT;
+        }
+        return -1;
+    }
 }
diff --git 
a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitSupportCookies.java
 
b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitSupportCookies.java
index 184c55b63..a0f7df7b8 100644
--- 
a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitSupportCookies.java
+++ 
b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitSupportCookies.java
@@ -13,9 +13,12 @@
  */
 package org.apache.shiro.ee.filters;
 
+import static org.apache.shiro.SecurityUtils.getSecurityManager;
 import static org.apache.shiro.ee.cdi.ShiroScopeContext.isWebContainerSessions;
 import static 
org.apache.shiro.ee.filters.FormResubmitSupport.getNativeSessionManager;
+import java.net.CookieManager;
 import java.net.HttpCookie;
+import java.net.URI;
 import java.time.Duration;
 import java.util.List;
 import java.util.Map;
@@ -24,12 +27,15 @@ import java.util.stream.Stream;
 import jakarta.servlet.ServletContext;
 import jakarta.servlet.ServletRequest;
 import jakarta.servlet.http.Cookie;
+import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
 import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.ee.listeners.EnvironmentLoaderListener;
+import static 
org.apache.shiro.web.mgt.CookieRememberMeManager.DEFAULT_REMEMBER_ME_COOKIE_NAME;
 import static 
org.apache.shiro.web.servlet.ShiroHttpSession.DEFAULT_SESSION_ID_NAME;
 
 /**
@@ -96,4 +102,30 @@ public class FormResubmitSupportCookies {
     static Stream<HttpCookie> cookieStreamFromHeader(@NonNull List<String> 
cookies) {
         return cookies.stream().map(HttpCookie::parse).map(list -> 
list.get(0));
     }
+
+    static void initializeCookies(URI savedRequest, ServletContext 
servletContext,
+                                  CookieManager cookieManager, 
HttpServletRequest originalRequest) {
+        var session = SecurityUtils.getSubject().getSession();
+        var sessionCookieName = getSessionCookieName(servletContext, 
getSecurityManager());
+        var sessionCookie = new HttpCookie(sessionCookieName, 
session.getId().toString());
+        sessionCookie.setPath(servletContext.getContextPath());
+        sessionCookie.setVersion(0);
+        cookieManager.getCookieStore().add(savedRequest, sessionCookie);
+        log.debug("Setting Cookie {}", sessionCookieName);
+        for (Cookie origCookie : originalRequest.getCookies()) {
+            if (!origCookie.getName().startsWith(sessionCookieName)
+                    && 
!origCookie.getName().equals(DEFAULT_REMEMBER_ME_COOKIE_NAME)) {
+                try {
+                    log.debug("Setting Cookie {}", origCookie.getName());
+                    HttpCookie cookie = new HttpCookie(origCookie.getName(), 
origCookie.getValue());
+                    cookie.setPath(servletContext.getContextPath());
+                    cookie.setVersion(0);
+                    cookieManager.getCookieStore().add(savedRequest, cookie);
+                } catch (IllegalArgumentException e) {
+                    log.warn("Form Resubmit: Ignoring invalid cookie [{} - 
{}]",
+                            origCookie.getName(), origCookie.getValue(), e);
+                }
+            }
+        }
+    }
 }
diff --git 
a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitValidator.java
 
b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitValidator.java
index 85b8f70aa..72ec2f547 100644
--- 
a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitValidator.java
+++ 
b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitValidator.java
@@ -23,9 +23,15 @@ import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.mgt.DefaultSecurityManager;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import static org.apache.shiro.SecurityUtils.getSecurityManager;
+import static org.apache.shiro.ee.filters.FormResubmitSupport.FORM_DATA_CACHE;
+import static org.apache.shiro.ee.filters.FormResubmitSupport.decrypt;
 import static 
org.apache.shiro.ee.filters.FormResubmitSupport.getRememberMeManager;
 import static 
org.apache.shiro.web.filter.authc.NoAccessFilter.FORM_RESUBMIT_CHECK_SERVLET_PATH;
 
@@ -33,25 +39,23 @@ import static 
org.apache.shiro.web.filter.authc.NoAccessFilter.FORM_RESUBMIT_CHE
 @WebServlet(name = "ShiroFormResubmitValidator", urlPatterns = 
FORM_RESUBMIT_CHECK_SERVLET_PATH)
 public class FormResubmitValidator extends HttpServlet {
     @Override
-    protected void doGet(HttpServletRequest request, HttpServletResponse 
response) {
-        var session = SecurityUtils.getSubject().getSession(false);
-        if (session == null) {
-            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
-            return;
-        }
-
+    protected void doPost(HttpServletRequest request, HttpServletResponse 
response) {
         var rememberMeManager = getRememberMeManager();
         if (rememberMeManager == null || rememberMeManager.getCipherService() 
== null) {
             response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
         } else {
             try {
-                String encryptedSessionId = 
rememberMeManager.getCipherService()
-                        
.encrypt(session.getId().toString().getBytes(StandardCharsets.UTF_8),
+                String formDataKey = 
decrypt(request.getReader().lines().collect(Collectors.joining()), 
rememberMeManager);
+                var cache = getSecurityManager(DefaultSecurityManager.class)
+                        .getCacheManager().getCache(FORM_DATA_CACHE);
+                
Optional.ofNullable(cache.get(UUID.fromString(formDataKey))).orElseThrow(IllegalCallerException::new);
+                String encryptedFormDataKey = 
rememberMeManager.getCipherService()
+                        .encrypt(formDataKey.getBytes(StandardCharsets.UTF_8),
                         rememberMeManager.getEncryptionCipherKey()).toBase64();
-                response.getWriter().write(encryptedSessionId);
+                response.getWriter().write(encryptedFormDataKey);
                 response.setStatus(HttpServletResponse.SC_OK);
-            } catch (IOException e) {
-                log.warn("Form resubmit verification: failed to write 
encrypted principals to response", e);
+            } catch (IOException | IllegalCallerException e) {
+                log.warn("Form resubmit verification: invalid input or failed 
to write encrypted session id to response", e);
                 response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
             }
         }
diff --git 
a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/ShiroFilter.java 
b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/ShiroFilter.java
index 76d0ead3f..9413d1261 100644
--- 
a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/ShiroFilter.java
+++ 
b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/ShiroFilter.java
@@ -233,7 +233,7 @@ public class ShiroFilter extends 
org.apache.shiro.web.servlet.ShiroFilter {
     }
 
     @Override
-    @SneakyThrows
+    @SneakyThrows(InterruptedException.class)
     protected void executeChain(ServletRequest request, ServletResponse 
response,
             FilterChain origChain) throws IOException, ServletException {
         if (isShiroEEDisabled(getServletContext())) {
@@ -245,7 +245,7 @@ public class ShiroFilter extends 
org.apache.shiro.web.servlet.ShiroFilter {
             log.debug("Resubmitting Post Data: {}", postData);
             var httpRequest = WebUtils.toHttp(request);
             boolean rememberedAjaxResubmit = 
"partial/ajax".equals(httpRequest.getHeader("Faces-Request"));
-            Optional.ofNullable(resubmitSavedForm(postData,
+            Optional.ofNullable(resubmitSavedForm(postData, null,
                     Servlets.getRequestURLWithQueryString(httpRequest),
                     WebUtils.toHttp(request), WebUtils.toHttp(response),
                     request.getServletContext(), rememberedAjaxResubmit, 
false))
diff --git 
a/web/src/main/java/org/apache/shiro/web/filter/authc/NoAccessFilter.java 
b/web/src/main/java/org/apache/shiro/web/filter/authc/NoAccessFilter.java
index db1fecdd5..4b2614234 100644
--- a/web/src/main/java/org/apache/shiro/web/filter/authc/NoAccessFilter.java
+++ b/web/src/main/java/org/apache/shiro/web/filter/authc/NoAccessFilter.java
@@ -52,7 +52,7 @@ public class NoAccessFilter extends AuthenticatingFilter {
     protected boolean isAccessAllowed(ServletRequest request, ServletResponse 
response, Object mappedValue) {
         if (request instanceof HttpServletRequest) {
             HttpServletRequest httpRequest = WebUtils.toHttp(request);
-            return httpRequest.getMethod().equals("GET")
+            return httpRequest.getMethod().equals("POST")
                     && 
httpRequest.getServletPath().equals(FORM_RESUBMIT_CHECK_SERVLET_PATH);
         }
         return false;
diff --git 
a/web/src/main/java/org/apache/shiro/web/servlet/ShiroHttpServletResponse.java 
b/web/src/main/java/org/apache/shiro/web/servlet/ShiroHttpServletResponse.java
index e645f7b36..c2c93ae3c 100644
--- 
a/web/src/main/java/org/apache/shiro/web/servlet/ShiroHttpServletResponse.java
+++ 
b/web/src/main/java/org/apache/shiro/web/servlet/ShiroHttpServletResponse.java
@@ -27,6 +27,10 @@ import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLEncoder;
+import static org.apache.shiro.web.filter.authz.PortFilter.DEFAULT_HTTP_PORT;
+import static org.apache.shiro.web.filter.authz.PortFilter.HTTP_SCHEME;
+import static org.apache.shiro.web.filter.authz.SslFilter.DEFAULT_HTTPS_PORT;
+import static org.apache.shiro.web.filter.authz.SslFilter.HTTPS_SCHEME;
 
 /**
  * HttpServletResponse implementation to support URL Encoding of Shiro Session 
IDs.
@@ -183,18 +187,18 @@ public class ShiroHttpServletResponse extends 
HttpServletResponseWrapper {
         }
         int serverPort = hreq.getServerPort();
         if (serverPort == -1) {
-            if ("https".equals(hreq.getScheme())) {
-                serverPort = 443;
+            if (HTTPS_SCHEME.equals(hreq.getScheme())) {
+                serverPort = DEFAULT_HTTPS_PORT;
             } else {
-                serverPort = 80;
+                serverPort = DEFAULT_HTTP_PORT;
             }
         }
         int urlPort = url.getPort();
         if (urlPort == -1) {
-            if ("https".equals(url.getProtocol())) {
-                urlPort = 443;
+            if (HTTPS_SCHEME.equals(url.getProtocol())) {
+                urlPort = DEFAULT_HTTPS_PORT;
             } else {
-                urlPort = 80;
+                urlPort = DEFAULT_HTTP_PORT;
             }
         }
         if (serverPort != urlPort) {
@@ -208,9 +212,7 @@ public class ShiroHttpServletResponse extends 
HttpServletResponseWrapper {
                 return (false);
             }
             String tok = ";" + DEFAULT_SESSION_ID_PARAMETER_NAME + "=" + 
session.getId();
-            if (file.indexOf(tok, contextPath.length()) >= 0) {
-                return (false);
-            }
+            return file.indexOf(tok, contextPath.length()) < 0;
         }
 
         // This URL belongs to our web application, so it is encodeable
@@ -248,8 +250,8 @@ public class ShiroHttpServletResponse extends 
HttpServletResponseWrapper {
 
             try {
                 buf.append(scheme).append("://").append(name);
-                if ((scheme.equals("http") && port != 80)
-                        || (scheme.equals("https") && port != 443)) {
+                if ((scheme.equals(HTTP_SCHEME) && port != DEFAULT_HTTP_PORT)
+                        || (scheme.equals(HTTPS_SCHEME) && port != 
DEFAULT_HTTPS_PORT)) {
                     buf.append(':').append(port);
                 }
                 if (!leadingSlash) {
@@ -262,9 +264,7 @@ public class ShiroHttpServletResponse extends 
HttpServletResponseWrapper {
                 }
                 buf.append(location);
             } catch (IOException e) {
-                IllegalArgumentException iae = new 
IllegalArgumentException(location);
-                iae.initCause(e);
-                throw iae;
+                throw new IllegalArgumentException(location, e);
             }
 
             return buf.toString();
@@ -333,7 +333,7 @@ public class ShiroHttpServletResponse extends 
HttpServletResponseWrapper {
         }
         StringBuilder sb = new StringBuilder(path);
         // session id param can't be first.
-        if (sb.length() > 0) {
+        if (!sb.isEmpty()) {
             sb.append(";");
             sb.append(DEFAULT_SESSION_ID_PARAMETER_NAME);
             sb.append("=");

Reply via email to