diff --git a/web/src/main/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServices.java b/web/src/main/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServices.java index 7e1369fa764..30b9e261d05 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServices.java +++ b/web/src/main/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServices.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Base64; +import java.util.function.Consumer; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; @@ -97,6 +98,9 @@ public abstract class AbstractRememberMeServices private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper(); + private Consumer cookieCustomizer = (cookie) -> { + }; + protected AbstractRememberMeServices(String key, UserDetailsService userDetailsService) { Assert.hasLength(key, "key cannot be empty or null"); Assert.notNull(userDetailsService, "UserDetailsService cannot be null"); @@ -373,6 +377,9 @@ protected void setCookie(String[] tokens, int maxAge, HttpServletRequest request } cookie.setSecure((this.useSecureCookie != null) ? this.useSecureCookie : request.isSecure()); cookie.setHttpOnly(true); + + this.cookieCustomizer.accept(cookie); + response.addCookie(cookie); } @@ -492,4 +499,14 @@ public void setMessageSource(MessageSource messageSource) { this.messages = new MessageSourceAccessor(messageSource); } + /** + * Sets the {@link Consumer}, allowing customization of cookie. + * @param cookieCustomizer customize for cookie + * @since 6.4 + */ + public void setCookieCustomizer(Consumer cookieCustomizer) { + Assert.notNull(cookieCustomizer, "cookieCustomizer cannot be null"); + this.cookieCustomizer = cookieCustomizer; + } + } diff --git a/web/src/test/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServicesTests.java b/web/src/test/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServicesTests.java index 541a685f562..ba399db5b13 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServicesTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/rememberme/AbstractRememberMeServicesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -402,6 +402,17 @@ public void setMessageSourceWhenNotNullThenCanGet() { verify(source).getMessage(eq(code), any(), any()); } + @Test + public void setCookieCustomAttribute() { + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockRememberMeServices services = new MockRememberMeServices(this.uds); + services.setCookieCustomizer((cookie) -> cookie.setAttribute("attr1", "value1")); + services.setCookie(new String[] { "mycookie" }, 1000, request, response); + Cookie cookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY); + assertThat(cookie.getAttribute("attr1")).isEqualTo("value1"); + } + private Cookie[] createLoginCookie(String cookieToken) { MockRememberMeServices services = new MockRememberMeServices(this.uds); Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,