diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurer.java index 85f95b27cd..1cbc09f608 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurer.java @@ -74,6 +74,7 @@ import org.springframework.security.web.authentication.ui.DefaultLoginPageGenera * * * @author Rob Winch + * @author Eddú Meléndez * @since 3.2 */ public final class RememberMeConfigurer> extends @@ -84,6 +85,7 @@ public final class RememberMeConfigurer> extend private LogoutHandler logoutHandler; private String rememberMeParameter = "remember-me"; private String rememberMeCookieName = "remember-me"; + private String rememberMeCookieDomain; private PersistentTokenRepository tokenRepository; private UserDetailsService userDetailsService; private Integer tokenValiditySeconds; @@ -192,6 +194,18 @@ public final class RememberMeConfigurer> extend return this; } + /** + * The domain name within which the remember me cookie is visible. + * + * @param rememberMeCookieDomain the domain name within which the remember me cookie is visible. + * @return the {@link RememberMeConfigurer} for further customization + * @since 4.1.0 + */ + public RememberMeConfigurer rememberMeCookieDomain(String rememberMeCookieDomain) { + this.rememberMeCookieDomain = rememberMeCookieDomain; + return this; + } + /** * Allows control over the destination a remembered user is sent to when they are * successfully authenticated. By default, the filter will just allow the current @@ -294,6 +308,9 @@ public final class RememberMeConfigurer> extend http, key); tokenRememberMeServices.setParameter(rememberMeParameter); tokenRememberMeServices.setCookieName(rememberMeCookieName); + if (rememberMeCookieDomain != null) { + tokenRememberMeServices.setCookieDomain(rememberMeCookieDomain); + } if (tokenValiditySeconds != null) { tokenRememberMeServices.setTokenValiditySeconds(tokenValiditySeconds); } diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.groovy index 3a00787b1d..abece7399a 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/RememberMeConfigurerTests.groovy @@ -18,7 +18,6 @@ package org.springframework.security.config.annotation.web.configurers import javax.servlet.http.Cookie import org.springframework.beans.factory.annotation.Autowired -import org.springframework.context.annotation.Configuration import org.springframework.mock.web.MockFilterChain import org.springframework.mock.web.MockHttpServletRequest import org.springframework.mock.web.MockHttpServletResponse @@ -28,7 +27,6 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider import org.springframework.security.config.annotation.AnyObjectPostProcessor import org.springframework.security.config.annotation.BaseSpringSpec import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder -import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer; import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter @@ -157,6 +155,23 @@ public class RememberMeConfigurerTests extends BaseSpringSpec { response.getRedirectedUrl() == "http://localhost/login" } + def "http/remember-me with cookied domain"() { + setup: + loadConfig(RememberMeCookieDomainConfig) + when: + super.setup() + request.servletPath = "/login" + request.method = "POST" + request.parameters.username = ["user"] as String[] + request.parameters.password = ["password"] as String[] + request.parameters.'remember-me' = ["true"] as String[] + springSecurityFilterChain.doFilter(request,response,chain) + Cookie rememberMeCookie = getRememberMeCookie() + then: "response contains remember me cookie" + rememberMeCookie != null + rememberMeCookie.domain == "spring.io" + } + @EnableWebSecurity static class RememberMeConfig extends WebSecurityConfigurerAdapter { protected void configure(HttpSecurity http) throws Exception { @@ -177,6 +192,27 @@ public class RememberMeConfigurerTests extends BaseSpringSpec { } } + @EnableWebSecurity + static class RememberMeCookieDomainConfig extends WebSecurityConfigurerAdapter { + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests() + .anyRequest().hasRole("USER") + .and() + .formLogin() + .and() + .rememberMe() + .rememberMeCookieDomain("spring.io") + } + + @Autowired + public void configureGlobal(AuthenticationManagerBuilder auth) { + auth + .inMemoryAuthentication() + .withUser("user").password("password").roles("USER"); + } + } + Cookie createRememberMeCookie() { MockHttpServletRequest request = new MockHttpServletRequest() MockHttpServletResponse response = new MockHttpServletResponse() 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 b7cf0f172d..91ca6a8b2d 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 @@ -50,6 +50,7 @@ import org.springframework.util.StringUtils; * * @author Luke Taylor * @author Rob Winch + * @author Eddú Meléndez * @since 2.0 */ public abstract class AbstractRememberMeServices implements RememberMeServices, @@ -75,6 +76,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices, private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); private String cookieName = SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY; + private String cookieDomain; private String parameter = DEFAULT_PARAMETER; private boolean alwaysRemember; private String key; @@ -385,7 +387,9 @@ public abstract class AbstractRememberMeServices implements RememberMeServices, Cookie cookie = new Cookie(cookieName, cookieValue); cookie.setMaxAge(maxAge); cookie.setPath(getCookiePath(request)); - + if (cookieDomain != null) { + cookie.setDomain(cookieDomain); + } if (maxAge < 1) { cookie.setVersion(1); } @@ -430,6 +434,11 @@ public abstract class AbstractRememberMeServices implements RememberMeServices, this.cookieName = cookieName; } + public void setCookieDomain(String cookieDomain) { + Assert.hasLength(cookieDomain, "Cookie domain cannot be empty or null"); + this.cookieDomain = cookieDomain; + } + protected String getCookieName() { return cookieName; } 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 7d376300bf..c7e95893e5 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 @@ -420,6 +420,21 @@ public class AbstractRememberMeServicesTests { assertThat(cookie.getVersion()).isEqualTo(0); } + @Test + public void setCookieDomainValue() { + MockRememberMeServices services = new MockRememberMeServices(); + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + + services.setCookieName("mycookiename"); + services.setCookieDomain("spring.io"); + services.setCookie(new String[] { "mycookie" }, 1000, request, response); + Cookie cookie = response.getCookie("mycookiename"); + + assertThat(cookie).isNotNull(); + assertThat(cookie.getDomain()).isEqualTo("spring.io"); + } + private Cookie[] createLoginCookie(String cookieToken) { MockRememberMeServices services = new MockRememberMeServices(uds); Cookie cookie = new Cookie(