This is an automated email from the ASF dual-hosted git repository. lprimak pushed a commit to branch 3.x in repository https://gitbox.apache.org/repos/asf/shiro.git
commit 4cf109fc916dcc765ee8ed1f95931726e174fd4c Author: lprimak <[email protected]> AuthorDate: Thu Apr 30 09:30:17 2026 -0500 enh: rememberMe cookie options --- .../shiro/ee/filters/FormResubmitSupport.java | 9 +----- .../org/apache/shiro/ee/filters/SslFilter.java | 2 +- .../ee/listeners/EnvironmentLoaderListener.java | 15 ++++++++-- .../apache/shiro/ee/listeners/IniEnvironment.java | 9 ++++++ .../web/config/AbstractShiroWebConfiguration.java | 4 +-- .../shiro/web/mgt/CookieRememberMeManager.java | 32 ++++++++++++++++++++++ 6 files changed, 58 insertions(+), 13 deletions(-) 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 049f84d12..f4ef06964 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 @@ -52,6 +52,7 @@ import java.nio.charset.StandardCharsets; import java.util.Optional; 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.mgt.CookieRememberMeManager.DEFAULT_REMEMBER_ME_COOKIE_NAME; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -353,14 +354,6 @@ public class FormResubmitSupport { } } - static boolean hasFacesContext() { - try { - return Faces.hasContext(); - } catch (Throwable e) { - return false; - } - } - static boolean isLoginUrl(HttpServletRequest request) { String loginUrl = (String) request.getAttribute(LOGIN_URL_ATTR_NAME); return loginUrl != null && request.getRequestURI().equals(request.getContextPath() + loginUrl); diff --git a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/SslFilter.java b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/SslFilter.java index ac8abe002..b10321056 100644 --- a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/SslFilter.java +++ b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/filters/SslFilter.java @@ -13,7 +13,7 @@ */ package org.apache.shiro.ee.filters; -import static org.apache.shiro.ee.filters.FormResubmitSupport.hasFacesContext; +import static org.apache.shiro.ee.listeners.IniEnvironment.hasFacesContext; import java.io.IOException; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; 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 764971d5e..d16169245 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 @@ -24,8 +24,13 @@ import jakarta.servlet.ServletContextListener; import jakarta.servlet.SessionTrackingMode; import jakarta.servlet.annotation.WebListener; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.web.env.EnvironmentLoader; import org.apache.shiro.web.env.WebEnvironment; +import org.apache.shiro.web.mgt.CookieRememberMeManager; +import org.omnifaces.util.Faces; +import static org.apache.shiro.ee.listeners.IniEnvironment.hasFacesContext; /** * Automatic, adds ability to disable via system property @@ -74,7 +79,7 @@ public class EnvironmentLoaderListener extends EnvironmentLoader implements Serv } @Override - @SuppressWarnings("checkstyle:NPathComplexity") + @SuppressWarnings({"checkstyle:NPathComplexity", "checkstyle:CyclomaticComplexity"}) public void contextInitialized(ServletContextEvent sce) { if (Boolean.parseBoolean(sce.getServletContext().getInitParameter(SHIRO_EE_DISABLED_PARAM))) { sce.getServletContext().setAttribute(SHIRO_EE_DISABLED_PARAM, Boolean.TRUE); @@ -108,7 +113,13 @@ public class EnvironmentLoaderListener extends EnvironmentLoader implements Serv modifySessionTrackingConfiguration(sce); } - initEnvironment(sce.getServletContext()); + WebEnvironment environment = initEnvironment(sce.getServletContext()); + if (hasFacesContext() && Faces.isDevelopment() + && SecurityUtils.unwrapSecurityManager(environment.getWebSecurityManager(), DefaultSecurityManager.class) + .getRememberMeManager() instanceof CookieRememberMeManager rememberMeManager + && !rememberMeManager.isSecureInDevMode()) { + rememberMeManager.getCookie().setSecure(false); + } } } diff --git a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/listeners/IniEnvironment.java b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/listeners/IniEnvironment.java index 04461e9c8..4d9154900 100644 --- a/support/jakarta-ee/src/main/java/org/apache/shiro/ee/listeners/IniEnvironment.java +++ b/support/jakarta-ee/src/main/java/org/apache/shiro/ee/listeners/IniEnvironment.java @@ -35,6 +35,7 @@ import org.apache.shiro.web.env.IniWebEnvironment; import org.apache.shiro.web.filter.mgt.DefaultFilter; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.omnifaces.util.Beans; +import org.omnifaces.util.Faces; import org.omnifaces.util.Lazy; import static org.omnifaces.util.Utils.isBlank; @@ -104,6 +105,14 @@ public class IniEnvironment extends IniWebEnvironment { } } + public static boolean hasFacesContext() { + try { + return Faces.hasContext(); + } catch (Throwable e) { + return false; + } + } + @Override protected Ini getFrameworkIni() { if (otherConfigLocation != null) { diff --git a/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebConfiguration.java b/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebConfiguration.java index 8e29dea6e..fb28fa2f0 100644 --- a/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebConfiguration.java +++ b/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebConfiguration.java @@ -83,10 +83,10 @@ public class AbstractShiroWebConfiguration extends AbstractShiroConfiguration { @Value("#{ @environment['shiro.rememberMeManager.cookie.domain'] ?: null }") protected String rememberMeCookieDomain; - @Value("#{ @environment['shiro.rememberMeManager.cookie.path'] ?: null }") + @Value("#{ @environment['shiro.rememberMeManager.cookie.path'] ?: T(org.apache.shiro.web.servlet.Cookie).ROOT_PATH } }") protected String rememberMeCookiePath; - @Value("#{ @environment['shiro.rememberMeManager.cookie.secure'] ?: false }") + @Value("#{ @environment['shiro.rememberMeManager.cookie.secure'] ?: true }") protected boolean rememberMeCookieSecure; @Value("#{ @environment['shiro.rememberMeManager.cookie.sameSite'] ?:" diff --git a/web/src/main/java/org/apache/shiro/web/mgt/CookieRememberMeManager.java b/web/src/main/java/org/apache/shiro/web/mgt/CookieRememberMeManager.java index 6695c89d5..732cbb9aa 100644 --- a/web/src/main/java/org/apache/shiro/web/mgt/CookieRememberMeManager.java +++ b/web/src/main/java/org/apache/shiro/web/mgt/CookieRememberMeManager.java @@ -76,10 +76,12 @@ public class CookieRememberMeManager extends AbstractRememberMeManager { * The default name of the underlying rememberMe cookie which is {@code rememberMe}. */ public static final String DEFAULT_REMEMBER_ME_COOKIE_NAME = "rememberMe"; + public static final String REMEMBER_ME_SECURE_COOKIE_DISABLED = "org.apache.shiro.rememberMe.secure.disabled"; private static final Logger LOGGER = LoggerFactory.getLogger(CookieRememberMeManager.class); private Cookie cookie; + private boolean secureInDevMode = true; /** * Constructs a new {@code CookieRememberMeManager} with a default {@code rememberMe} cookie template. @@ -128,6 +130,33 @@ public class CookieRememberMeManager extends AbstractRememberMeManager { this.cookie = cookie; } + /** + * Returns {@code true} if the rememberMe cookie should be marked as secure when the application is running in + * development mode. This is a convenience method that allows users to set the rememberMe cookie as secure in + * production environments but not in development environments where SSL is often not used. + * The default value is {@code true}. + * @return {@code true} if the rememberMe cookie should be marked as secure when the application is running in + * development mode, {@code false} otherwise (the default value is {@code true}). + * @since 2.1.1 + */ + @SuppressWarnings("UnusedDeclaration") + public boolean isSecureInDevMode() { + return secureInDevMode; + } + + /** + * Sets whether the rememberMe cookie should be marked as secure when the application is running in development mode. + * This is a convenience method that allows users to set the rememberMe cookie as secure in production environments but not + * in development environments where SSL is often not used. The default value is {@code true}. + * @param secureInDevMode set to {@code true} if the rememberMe cookie should be marked as secure when the + * application is running in development mode, {@code false} otherwise (the default value is {@code true}). + * @since 2.1.1 + */ + @SuppressWarnings("UnusedDeclaration") + public void setSecureInDevMode(boolean secureInDevMode) { + this.secureInDevMode = secureInDevMode; + } + /** * Base64-encodes the specified serialized byte array and sets that base64-encoded String as the cookie value. * <p/> @@ -313,6 +342,9 @@ public class CookieRememberMeManager extends AbstractRememberMeManager { private Cookie createDefaultCookie() { Cookie cookie = new SimpleCookie(DEFAULT_REMEMBER_ME_COOKIE_NAME); cookie.setHttpOnly(true); + if (!Boolean.getBoolean(REMEMBER_ME_SECURE_COOKIE_DISABLED)) { + cookie.setSecure(true); + } //One year should be long enough - most sites won't object to requiring a user to log in if they haven't visited //in a year: cookie.setMaxAge(Cookie.ONE_YEAR);
