|
39 | 39 | import java.util.Collections; |
40 | 40 | import org.apache.shiro.crypto.CryptoException; |
41 | 41 | import org.apache.shiro.ee.filters.Forms.FallbackPredicate; |
| 42 | +import static org.apache.shiro.ee.filters.FormResubmitSupportCookies.initializeCookies; |
42 | 43 | import static org.apache.shiro.ee.filters.FormResubmitSupportCookies.transformCookieHeader; |
43 | 44 | import static org.apache.shiro.ee.listeners.EnvironmentLoaderListener.isFormResubmitDisabled; |
44 | 45 | import java.io.IOException; |
45 | 46 | import java.net.CookieManager; |
46 | | -import java.net.HttpCookie; |
47 | 47 | import java.net.URI; |
48 | 48 | import java.net.URLDecoder; |
49 | 49 | import java.net.http.HttpClient; |
50 | 50 | import java.net.http.HttpHeaders; |
51 | 51 | import java.net.http.HttpRequest; |
52 | 52 | import java.net.http.HttpResponse; |
53 | 53 | import java.nio.charset.StandardCharsets; |
| 54 | +import java.util.List; |
54 | 55 | import java.util.Objects; |
55 | 56 | import java.util.Optional; |
56 | 57 | import java.util.UUID; |
|
61 | 62 | import static org.apache.shiro.web.filter.authz.PortFilter.HTTP_SCHEME; |
62 | 63 | import static org.apache.shiro.web.filter.authz.SslFilter.DEFAULT_HTTPS_PORT; |
63 | 64 | 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; |
65 | 65 | import java.util.concurrent.atomic.AtomicReference; |
66 | 66 | import java.util.function.Consumer; |
67 | 67 | import java.util.regex.Pattern; |
68 | 68 | import java.util.stream.Collectors; |
69 | 69 | import jakarta.servlet.ServletContext; |
70 | 70 | import jakarta.servlet.ServletRequest; |
71 | | -import jakarta.servlet.http.Cookie; |
72 | 71 | import jakarta.servlet.http.HttpServletRequest; |
73 | 72 | import jakarta.servlet.http.HttpServletResponse; |
74 | 73 | import lombok.AccessLevel; |
|
79 | 78 | import lombok.SneakyThrows; |
80 | 79 | import lombok.ToString; |
81 | 80 | import lombok.extern.slf4j.Slf4j; |
82 | | -import org.apache.shiro.SecurityUtils; |
83 | 81 | import org.apache.shiro.cache.Cache; |
84 | 82 | import org.apache.shiro.lang.codec.Base64; |
85 | 83 | import org.apache.shiro.mgt.AbstractRememberMeManager; |
@@ -138,6 +136,11 @@ public class FormResubmitSupport { |
138 | 136 | private static final long DEFAULT_RESUBMIT_BLACK_LIST_TTL_SECONDS = 60L; |
139 | 137 | private static final String SEC_FETCH_SITE = "Sec-Fetch-Site"; |
140 | 138 | 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"; |
141 | 144 |
|
142 | 145 | static class HttpMethod { |
143 | 146 | static final String GET = "GET"; |
@@ -445,6 +448,7 @@ static String resubmitSavedForm(@NonNull String savedFormData, String savedFormD |
445 | 448 | } |
446 | 449 | if (Boolean.TRUE.toString().equals(originalRequest.getHeader(FORM_IS_RESUBMITTED))) { |
447 | 450 | log.debug("Form resubmit: internal auth failure"); |
| 451 | + setNoStoreHeaders(originalResponse); |
448 | 452 | originalResponse.setStatus(AUTHFAIL); |
449 | 453 | return resubmitResponseCleanup(originalRequest); |
450 | 454 | } |
@@ -554,6 +558,7 @@ private static String processResubmitResponse(HttpResponse<String> response, |
554 | 558 | originalResponse.setHeader(LOCATION, response.headers().firstValue(LOCATION).orElseThrow()); |
555 | 559 | } |
556 | 560 | case OK: |
| 561 | + propagateCacheHeaders(response, originalResponse); |
557 | 562 | // do not duplicate the session cookie(s) |
558 | 563 | transformCookieHeader(headers.allValues(SET_COOKIE)) |
559 | 564 | .entrySet().stream().filter(not(entry -> entry.getKey() |
@@ -583,32 +588,31 @@ private static String resubmitResponseCleanup(HttpServletRequest originalRequest |
583 | 588 | return null; |
584 | 589 | } |
585 | 590 |
|
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)); |
609 | 607 | } |
610 | 608 | } |
611 | 609 |
|
| 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 | + |
612 | 616 | private static boolean checkWhitelist(ServletContext servletContext, URI savedRequestURI, HttpClient client, |
613 | 617 | String savedFormDataKey) { |
614 | 618 | if (!isSecurityManagerTypeOf(getSecurityManager(), DefaultSecurityManager.class)) { |
|
0 commit comments