Skip to content

Commit 1c50535

Browse files
committed
bugfix: improve cache header handling for form resubmission responses
1 parent 353eda6 commit 1c50535

2 files changed

Lines changed: 63 additions & 27 deletions

File tree

support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitSupport.java

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,19 @@
3939
import java.util.Collections;
4040
import org.apache.shiro.crypto.CryptoException;
4141
import org.apache.shiro.ee.filters.Forms.FallbackPredicate;
42+
import static org.apache.shiro.ee.filters.FormResubmitSupportCookies.initializeCookies;
4243
import static org.apache.shiro.ee.filters.FormResubmitSupportCookies.transformCookieHeader;
4344
import static org.apache.shiro.ee.listeners.EnvironmentLoaderListener.isFormResubmitDisabled;
4445
import java.io.IOException;
4546
import java.net.CookieManager;
46-
import java.net.HttpCookie;
4747
import java.net.URI;
4848
import java.net.URLDecoder;
4949
import java.net.http.HttpClient;
5050
import java.net.http.HttpHeaders;
5151
import java.net.http.HttpRequest;
5252
import java.net.http.HttpResponse;
5353
import java.nio.charset.StandardCharsets;
54+
import java.util.List;
5455
import java.util.Objects;
5556
import java.util.Optional;
5657
import java.util.UUID;
@@ -61,14 +62,12 @@
6162
import static org.apache.shiro.web.filter.authz.PortFilter.HTTP_SCHEME;
6263
import static org.apache.shiro.web.filter.authz.SslFilter.DEFAULT_HTTPS_PORT;
6364
import static org.apache.shiro.web.filter.authz.SslFilter.HTTPS_SCHEME;
64-
import static org.apache.shiro.web.mgt.CookieRememberMeManager.DEFAULT_REMEMBER_ME_COOKIE_NAME;
6565
import java.util.concurrent.atomic.AtomicReference;
6666
import java.util.function.Consumer;
6767
import java.util.regex.Pattern;
6868
import java.util.stream.Collectors;
6969
import jakarta.servlet.ServletContext;
7070
import jakarta.servlet.ServletRequest;
71-
import jakarta.servlet.http.Cookie;
7271
import jakarta.servlet.http.HttpServletRequest;
7372
import jakarta.servlet.http.HttpServletResponse;
7473
import lombok.AccessLevel;
@@ -79,7 +78,6 @@
7978
import lombok.SneakyThrows;
8079
import lombok.ToString;
8180
import lombok.extern.slf4j.Slf4j;
82-
import org.apache.shiro.SecurityUtils;
8381
import org.apache.shiro.cache.Cache;
8482
import org.apache.shiro.lang.codec.Base64;
8583
import org.apache.shiro.mgt.AbstractRememberMeManager;
@@ -138,6 +136,11 @@ public class FormResubmitSupport {
138136
private static final long DEFAULT_RESUBMIT_BLACK_LIST_TTL_SECONDS = 60L;
139137
private static final String SEC_FETCH_SITE = "Sec-Fetch-Site";
140138
private static final String ORIGIN = "Origin";
139+
private static final String CACHE_CONTROL = "Cache-Control";
140+
private static final String NO_STORE = "no-store";
141+
private static final String PRAGMA = "Pragma";
142+
private static final String EXPIRES = "Expires";
143+
private static final String NO_CACHE = "no-cache";
141144

142145
static class HttpMethod {
143146
static final String GET = "GET";
@@ -445,6 +448,7 @@ static String resubmitSavedForm(@NonNull String savedFormData, String savedFormD
445448
}
446449
if (Boolean.TRUE.toString().equals(originalRequest.getHeader(FORM_IS_RESUBMITTED))) {
447450
log.debug("Form resubmit: internal auth failure");
451+
setNoStoreHeaders(originalResponse);
448452
originalResponse.setStatus(AUTHFAIL);
449453
return resubmitResponseCleanup(originalRequest);
450454
}
@@ -554,6 +558,7 @@ private static String processResubmitResponse(HttpResponse<String> response,
554558
originalResponse.setHeader(LOCATION, response.headers().firstValue(LOCATION).orElseThrow());
555559
}
556560
case OK:
561+
propagateCacheHeaders(response, originalResponse);
557562
// do not duplicate the session cookie(s)
558563
transformCookieHeader(headers.allValues(SET_COOKIE))
559564
.entrySet().stream().filter(not(entry -> entry.getKey()
@@ -583,32 +588,31 @@ private static String resubmitResponseCleanup(HttpServletRequest originalRequest
583588
return null;
584589
}
585590

586-
private static void initializeCookies(URI savedRequest, ServletContext servletContext,
587-
CookieManager cookieManager, HttpServletRequest originalRequest) {
588-
var session = SecurityUtils.getSubject().getSession();
589-
var sessionCookieName = getSessionCookieName(servletContext, getSecurityManager());
590-
var sessionCookie = new HttpCookie(sessionCookieName, session.getId().toString());
591-
sessionCookie.setPath(servletContext.getContextPath());
592-
sessionCookie.setVersion(0);
593-
cookieManager.getCookieStore().add(savedRequest, sessionCookie);
594-
log.debug("Setting Cookie {}", sessionCookieName);
595-
for (Cookie origCookie : originalRequest.getCookies()) {
596-
if (!origCookie.getName().startsWith(sessionCookieName)
597-
&& !origCookie.getName().equals(DEFAULT_REMEMBER_ME_COOKIE_NAME)) {
598-
try {
599-
log.debug("Setting Cookie {}", origCookie.getName());
600-
HttpCookie cookie = new HttpCookie(origCookie.getName(), origCookie.getValue());
601-
cookie.setPath(servletContext.getContextPath());
602-
cookie.setVersion(0);
603-
cookieManager.getCookieStore().add(savedRequest, cookie);
604-
} catch (IllegalArgumentException e) {
605-
log.warn("Form Resubmit: Ignoring invalid cookie [{} - {}]",
606-
origCookie.getName(), origCookie.getValue(), e);
607-
}
608-
}
591+
private static void propagateCacheHeaders(HttpResponse<String> response, HttpServletResponse originalResponse) {
592+
HttpHeaders upstreamHeaders = response.headers();
593+
594+
List<String> cacheControlValues = upstreamHeaders.allValues(CACHE_CONTROL);
595+
originalResponse.setHeader(CACHE_CONTROL, cacheControlValues.isEmpty()
596+
? NO_STORE : String.join(", ", cacheControlValues));
597+
598+
List<String> pragmaValues = upstreamHeaders.allValues(PRAGMA);
599+
originalResponse.setHeader(PRAGMA, pragmaValues.isEmpty()
600+
? NO_CACHE : String.join(", ", pragmaValues));
601+
602+
List<String> expiresValues = upstreamHeaders.allValues(EXPIRES);
603+
if (expiresValues.isEmpty()) {
604+
originalResponse.setDateHeader(EXPIRES, 0);
605+
} else {
606+
originalResponse.setHeader(EXPIRES, expiresValues.get(expiresValues.size() - 1));
609607
}
610608
}
611609

610+
private static void setNoStoreHeaders(HttpServletResponse response) {
611+
response.setHeader(CACHE_CONTROL, NO_STORE);
612+
response.setHeader(PRAGMA, NO_CACHE);
613+
response.setDateHeader(EXPIRES, 0);
614+
}
615+
612616
private static boolean checkWhitelist(ServletContext servletContext, URI savedRequestURI, HttpClient client,
613617
String savedFormDataKey) {
614618
if (!isSecurityManagerTypeOf(getSecurityManager(), DefaultSecurityManager.class)) {

support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/FormResubmitSupportCookies.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
*/
1414
package org.apache.shiro.ee.filters;
1515

16+
import static org.apache.shiro.SecurityUtils.getSecurityManager;
1617
import static org.apache.shiro.ee.cdi.ShiroScopeContext.isWebContainerSessions;
1718
import static org.apache.shiro.ee.filters.FormResubmitSupport.getNativeSessionManager;
19+
import java.net.CookieManager;
1820
import java.net.HttpCookie;
21+
import java.net.URI;
1922
import java.time.Duration;
2023
import java.util.List;
2124
import java.util.Map;
@@ -24,12 +27,15 @@
2427
import jakarta.servlet.ServletContext;
2528
import jakarta.servlet.ServletRequest;
2629
import jakarta.servlet.http.Cookie;
30+
import jakarta.servlet.http.HttpServletRequest;
2731
import jakarta.servlet.http.HttpServletResponse;
2832
import lombok.AccessLevel;
2933
import lombok.NoArgsConstructor;
3034
import lombok.NonNull;
3135
import lombok.extern.slf4j.Slf4j;
36+
import org.apache.shiro.SecurityUtils;
3237
import org.apache.shiro.ee.listeners.EnvironmentLoaderListener;
38+
import static org.apache.shiro.web.mgt.CookieRememberMeManager.DEFAULT_REMEMBER_ME_COOKIE_NAME;
3339
import static org.apache.shiro.web.servlet.ShiroHttpSession.DEFAULT_SESSION_ID_NAME;
3440

3541
/**
@@ -96,4 +102,30 @@ static Map<String, String> transformCookieHeader(@NonNull List<String> cookies)
96102
static Stream<HttpCookie> cookieStreamFromHeader(@NonNull List<String> cookies) {
97103
return cookies.stream().map(HttpCookie::parse).map(list -> list.get(0));
98104
}
105+
106+
static void initializeCookies(URI savedRequest, ServletContext servletContext,
107+
CookieManager cookieManager, HttpServletRequest originalRequest) {
108+
var session = SecurityUtils.getSubject().getSession();
109+
var sessionCookieName = getSessionCookieName(servletContext, getSecurityManager());
110+
var sessionCookie = new HttpCookie(sessionCookieName, session.getId().toString());
111+
sessionCookie.setPath(servletContext.getContextPath());
112+
sessionCookie.setVersion(0);
113+
cookieManager.getCookieStore().add(savedRequest, sessionCookie);
114+
log.debug("Setting Cookie {}", sessionCookieName);
115+
for (Cookie origCookie : originalRequest.getCookies()) {
116+
if (!origCookie.getName().startsWith(sessionCookieName)
117+
&& !origCookie.getName().equals(DEFAULT_REMEMBER_ME_COOKIE_NAME)) {
118+
try {
119+
log.debug("Setting Cookie {}", origCookie.getName());
120+
HttpCookie cookie = new HttpCookie(origCookie.getName(), origCookie.getValue());
121+
cookie.setPath(servletContext.getContextPath());
122+
cookie.setVersion(0);
123+
cookieManager.getCookieStore().add(savedRequest, cookie);
124+
} catch (IllegalArgumentException e) {
125+
log.warn("Form Resubmit: Ignoring invalid cookie [{} - {}]",
126+
origCookie.getName(), origCookie.getValue(), e);
127+
}
128+
}
129+
}
130+
}
99131
}

0 commit comments

Comments
 (0)