From 11d12a1bdc45872731b34cf68af086945ad2f735 Mon Sep 17 00:00:00 2001 From: lprimak Date: Sun, 16 Aug 2026 22:27:18 -0500 Subject: [PATCH 1/2] bugfix(jakarta-ee): missing cache entry for the check --- .../filters/AuthenticationFilterDelegate.java | 8 +++++++- .../shiro/ee/filters/FormResubmitSupport.java | 20 +++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/AuthenticationFilterDelegate.java b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/AuthenticationFilterDelegate.java index 7cb31df49f..fcc63912a5 100644 --- a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/AuthenticationFilterDelegate.java +++ b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/AuthenticationFilterDelegate.java @@ -33,12 +33,15 @@ import static org.apache.shiro.ee.filters.FormAuthenticationFilter.LOGIN_URL_ATTR_NAME; import static org.apache.shiro.ee.filters.FormAuthenticationFilter.LOGIN_WAITTIME_ATTR_NAME; import static org.apache.shiro.ee.filters.FormAuthenticationFilter.NO_PREDICATE; +import static org.apache.shiro.ee.filters.FormAuthenticationFilter.getPathWithinApplication; +import static org.apache.shiro.ee.filters.FormResubmitSupport.isPostRequest; import static org.apache.shiro.ee.filters.FormResubmitSupport.savePostDataForResubmit; import static org.apache.shiro.ee.filters.FormResubmitSupport.saveRequestReferer; import static org.apache.shiro.ee.filters.LogoutFilter.LOGOUT_PREDICATE_ATTR_NAME; import static org.apache.shiro.ee.filters.LogoutFilter.YES_PREDICATE; import static org.apache.shiro.ee.listeners.EnvironmentLoaderListener.isFormResubmitDisabled; import static org.apache.shiro.ee.listeners.EnvironmentLoaderListener.isServletNoPrincipal; +import static org.apache.shiro.web.filter.authc.NoAccessFilter.FORM_RESUBMIT_CHECK_SERVLET_PATH; import static org.apache.shiro.web.jaxrs.SubjectPrincipalRequestFilter.SHIRO_WEB_JAXRS_DISABLE_PRINCIPAL_PARAM; /** @@ -101,7 +104,10 @@ public boolean preHandle(ServletRequest request, ServletResponse response) throw public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { Subject subject = methods.getSubject(request, response); boolean isAuthenticated = subject.isAuthenticated() && subject.getPrincipal() != null; - return isAuthenticated || (useRemembered && subject.isRemembered()); + return isAuthenticated || (useRemembered && subject.isRemembered()) + || (isPostRequest(request) + && FORM_RESUBMIT_CHECK_SERVLET_PATH.equals(getPathWithinApplication(request, + () -> WebUtils.getPathWithinApplication(WebUtils.toHttp(request))))); } /** 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 44f1cf56d7..f9455a3e8a 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 @@ -37,6 +37,7 @@ import java.net.URISyntaxException; import java.time.Duration; import java.util.Collections; +import org.apache.shiro.cache.CacheManager; import org.apache.shiro.crypto.CryptoException; import org.apache.shiro.ee.filters.Forms.FallbackPredicate; import static org.apache.shiro.ee.filters.FormResubmitSupportCookies.initializeCookies; @@ -646,7 +647,8 @@ private static boolean checkWhitelist(ServletContext servletContext, URI savedRe } else if (isBlacklisted(blacklist, authority)) { log.debug("Form resubmit blacklist cache hit for {}", savedRequestURI); return false; - } else if (checkWhitelistClient(savedRequestURI, servletContext.getContextPath(), client, savedFormDataKey)) { + } else if (checkWhitelistClient(savedRequestURI, servletContext.getContextPath(), client, + savedFormDataKey, dsm.getCacheManager())) { putWhitelistEntry(whitelist, authority); blacklist.remove(authority); return true; @@ -710,7 +712,10 @@ static boolean isBlacklisted(Cache blacklist, String authority, } private static boolean checkWhitelistClient(URI savedRequestURI, String contextPath, HttpClient client, - String savedFormDataKey) { + String savedFormDataKey, @NonNull CacheManager cacheManager) { + Cache cache = null; + UUID savedFormDataUUID = null; + try { var rememberMeManager = getRememberMeManager(); if (rememberMeManager == null || rememberMeManager.getCipherService() == null @@ -719,6 +724,13 @@ private static boolean checkWhitelistClient(URI savedRequestURI, String contextP return false; } + if (savedFormDataKey == null) { + savedFormDataUUID = UUID.randomUUID(); + savedFormDataKey = savedFormDataUUID.toString(); + cache = cacheManager.getCache(FORM_DATA_CACHE); + cache.put(savedFormDataUUID, "__DUMMY_FOR_CLIENT_WHITELIST_CHECK__"); + } + var request = HttpRequest.newBuilder() .uri(URI.create("%s://%s%s%s".formatted(savedRequestURI.getScheme(), savedRequestURI.getAuthority(), contextPath, FORM_RESUBMIT_CHECK_SERVLET_PATH))) @@ -738,6 +750,10 @@ private static boolean checkWhitelistClient(URI savedRequestURI, String contextP } catch (IOException | InterruptedException e) { log.debug("Form resubmit whitelist check failed for {} with exception: {}", savedRequestURI, e); + } finally { + if (cache != null) { + cache.remove(savedFormDataUUID); + } } return false; } From 524eb005f373815ebd8d6d3a788e3336d7e7e1e9 Mon Sep 17 00:00:00 2001 From: lprimak Date: Mon, 17 Aug 2026 09:28:15 -0500 Subject: [PATCH 2/2] now covering the failed path with integration tests --- .../jakarta-ee/src/main/webapp/WEB-INF/web.xml | 4 ++++ .../shiro/ee/filters/FormResubmitSupport.java | 10 ++++++++-- .../ee/listeners/EnvironmentLoaderListener.java | 16 ++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/web.xml b/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/web.xml index cead2eaece..3008d063e4 100644 --- a/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/web.xml +++ b/integration-tests/jakarta-ee/src/main/webapp/WEB-INF/web.xml @@ -70,6 +70,10 @@ org.apache.shiro.form-resubmit.secure-cookies false + + org.apache.shiro.form-resubmit.whitelist.disabled + true + 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 f9455a3e8a..20e8b6108f 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 @@ -42,6 +42,7 @@ 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.isFormResubmitBlacklistEnabled; import static org.apache.shiro.ee.listeners.EnvironmentLoaderListener.isFormResubmitDisabled; import java.io.IOException; import java.net.CookieManager; @@ -58,6 +59,7 @@ import java.util.Set; import java.util.UUID; import static java.util.function.Predicate.not; +import static org.apache.shiro.ee.listeners.EnvironmentLoaderListener.isFormResubmitWhitelistEnabled; 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.filter.authz.PortFilter.DEFAULT_HTTP_PORT; @@ -649,12 +651,16 @@ private static boolean checkWhitelist(ServletContext servletContext, URI savedRe return false; } else if (checkWhitelistClient(savedRequestURI, servletContext.getContextPath(), client, savedFormDataKey, dsm.getCacheManager())) { - putWhitelistEntry(whitelist, authority); + if (isFormResubmitWhitelistEnabled(servletContext)) { + putWhitelistEntry(whitelist, authority); + } blacklist.remove(authority); return true; } - putBlacklistEntry(blacklist, authority); + if (isFormResubmitBlacklistEnabled(servletContext)) { + putBlacklistEntry(blacklist, authority); + } return false; } diff --git a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/listeners/EnvironmentLoaderListener.java b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/listeners/EnvironmentLoaderListener.java index fa4b0a08f0..5d49862ec5 100644 --- a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/listeners/EnvironmentLoaderListener.java +++ b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/listeners/EnvironmentLoaderListener.java @@ -46,6 +46,8 @@ public class EnvironmentLoaderListener extends EnvironmentLoader implements Serv private static final String SHIRO_EE_CHAR_ENCODING_PARAM = "org.apache.shiro.ee.character-encoding"; private static final String FORM_RESUBMIT_DISABLED_PARAM = "org.apache.shiro.form-resubmit.disabled"; private static final String FORM_RESUBMIT_SECURE_COOKIES = "org.apache.shiro.form-resubmit.secure-cookies"; + private static final String FORM_RESUBMIT_WHITE_LIST_DISABLED = "org.apache.shiro.form-resubmit.whitelist.disabled"; + private static final String FORM_RESUBMIT_BLACK_LIST_DISABLED = "org.apache.shiro.form-resubmit.blacklist.disabled"; private static final String SHIRO_WEB_DISABLE_PRINCIPAL_PARAM = "org.apache.shiro.web.disable-principal"; public static boolean isShiroEEDisabled(ServletContext ctx) { @@ -64,6 +66,14 @@ public static boolean isFormResubmitSecureCookies(ServletContext ctx) { return Boolean.TRUE.equals(ctx.getAttribute(FORM_RESUBMIT_SECURE_COOKIES)); } + public static boolean isFormResubmitWhitelistEnabled(ServletContext ctx) { + return !Boolean.TRUE.equals(ctx.getAttribute(FORM_RESUBMIT_WHITE_LIST_DISABLED)); + } + + public static boolean isFormResubmitBlacklistEnabled(ServletContext ctx) { + return !Boolean.TRUE.equals(ctx.getAttribute(FORM_RESUBMIT_BLACK_LIST_DISABLED)); + } + public static boolean isServletNoPrincipal(ServletContext ctx) { return Boolean.TRUE.equals(ctx.getAttribute(SHIRO_WEB_DISABLE_PRINCIPAL_PARAM)); } @@ -96,6 +106,12 @@ public void contextInitialized(ServletContextEvent sce) { } else { sce.getServletContext().setAttribute(FORM_RESUBMIT_SECURE_COOKIES, Boolean.FALSE); } + if (Boolean.parseBoolean(sce.getServletContext().getInitParameter(FORM_RESUBMIT_WHITE_LIST_DISABLED))) { + sce.getServletContext().setAttribute(FORM_RESUBMIT_WHITE_LIST_DISABLED, Boolean.TRUE); + } + if (Boolean.parseBoolean(sce.getServletContext().getInitParameter(FORM_RESUBMIT_BLACK_LIST_DISABLED))) { + sce.getServletContext().setAttribute(FORM_RESUBMIT_BLACK_LIST_DISABLED, Boolean.TRUE); + } if (Boolean.parseBoolean(sce.getServletContext().getInitParameter(SHIRO_WEB_DISABLE_PRINCIPAL_PARAM))) { sce.getServletContext().setAttribute(SHIRO_WEB_DISABLE_PRINCIPAL_PARAM, Boolean.TRUE); }