diff --git a/spring-session/build.gradle b/spring-session/build.gradle index e2902c09..6c7cb3fd 100644 --- a/spring-session/build.gradle +++ b/spring-session/build.gradle @@ -25,7 +25,8 @@ dependencies { "org.springframework:spring-web:$springVersion", "org.springframework:spring-messaging:$springVersion", "org.springframework:spring-websocket:$springVersion", - "org.springframework.security:spring-security-core:$springSecurityVersion" + "org.springframework.security:spring-security-core:$springSecurityVersion", + "org.springframework.security:spring-security-web:$springSecurityVersion" provided "javax.servlet:javax.servlet-api:$servletApiVersion" integrationTestCompile "redis.clients:jedis:$jedisVersion", "org.apache.commons:commons-pool2:2.2", diff --git a/spring-session/src/main/java/org/springframework/session/config/annotation/web/http/SpringHttpSessionConfiguration.java b/spring-session/src/main/java/org/springframework/session/config/annotation/web/http/SpringHttpSessionConfiguration.java index 516b8115..e4f2f35c 100644 --- a/spring-session/src/main/java/org/springframework/session/config/annotation/web/http/SpringHttpSessionConfiguration.java +++ b/spring-session/src/main/java/org/springframework/session/config/annotation/web/http/SpringHttpSessionConfiguration.java @@ -19,22 +19,29 @@ package org.springframework.session.config.annotation.web.http; import java.util.ArrayList; import java.util.List; +import javax.annotation.PostConstruct; import javax.servlet.ServletContext; import javax.servlet.http.HttpSessionListener; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.session.ExpiringSession; import org.springframework.session.SessionRepository; import org.springframework.session.events.SessionCreatedEvent; import org.springframework.session.events.SessionDestroyedEvent; +import org.springframework.session.security.SpringSessionRememberMeServices; import org.springframework.session.web.http.CookieHttpSessionStrategy; import org.springframework.session.web.http.CookieSerializer; +import org.springframework.session.web.http.DefaultCookieSerializer; import org.springframework.session.web.http.HttpSessionStrategy; import org.springframework.session.web.http.MultiHttpSessionStrategy; import org.springframework.session.web.http.SessionEventHttpSessionListenerAdapter; import org.springframework.session.web.http.SessionRepositoryFilter; +import org.springframework.util.ClassUtils; /** * Configures the basics for setting up Spring Session in a web environment. In order to @@ -75,20 +82,38 @@ import org.springframework.session.web.http.SessionRepositoryFilter; * * * @author Rob Winch + * @author Vedran Pavic * @since 1.1 * * @see EnableSpringHttpSession */ @Configuration -public class SpringHttpSessionConfiguration { +public class SpringHttpSessionConfiguration implements ApplicationContextAware { private CookieHttpSessionStrategy defaultHttpSessionStrategy = new CookieHttpSessionStrategy(); + private boolean usesSpringSessionRememberMeServices; + + private ServletContext servletContext; + + private CookieSerializer cookieSerializer; + private HttpSessionStrategy httpSessionStrategy = this.defaultHttpSessionStrategy; private List httpSessionListeners = new ArrayList(); - private ServletContext servletContext; + @PostConstruct + public void init() { + if (this.cookieSerializer != null) { + this.defaultHttpSessionStrategy.setCookieSerializer(this.cookieSerializer); + } + else if (this.usesSpringSessionRememberMeServices) { + DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer(); + cookieSerializer.setRememberMeRequestAttribute( + SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR); + this.defaultHttpSessionStrategy.setCookieSerializer(cookieSerializer); + } + } @Bean public SessionEventHttpSessionListenerAdapter sessionEventHttpSessionListenerAdapter() { @@ -111,6 +136,15 @@ public class SpringHttpSessionConfiguration { return sessionRepositoryFilter; } + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + if (ClassUtils.isPresent("org.springframework.security.web.authentication." + + "RememberMeServices", null)) { + this.usesSpringSessionRememberMeServices = !applicationContext + .getBeansOfType(SpringSessionRememberMeServices.class).isEmpty(); + } + } + @Autowired(required = false) public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; @@ -118,7 +152,7 @@ public class SpringHttpSessionConfiguration { @Autowired(required = false) public void setCookieSerializer(CookieSerializer cookieSerializer) { - this.defaultHttpSessionStrategy.setCookieSerializer(cookieSerializer); + this.cookieSerializer = cookieSerializer; } @Autowired(required = false) @@ -130,4 +164,5 @@ public class SpringHttpSessionConfiguration { public void setHttpSessionListeners(List listeners) { this.httpSessionListeners = listeners; } + } diff --git a/spring-session/src/main/java/org/springframework/session/security/SpringSessionRememberMeServices.java b/spring-session/src/main/java/org/springframework/session/security/SpringSessionRememberMeServices.java new file mode 100644 index 00000000..93eccecc --- /dev/null +++ b/spring-session/src/main/java/org/springframework/session/security/SpringSessionRememberMeServices.java @@ -0,0 +1,124 @@ +/* + * Copyright 2014-2016 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.session.security; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.RememberMeServices; +import org.springframework.util.Assert; + +/** + * A {@link RememberMeServices} implementation that uses Spring Session backed + * {@link HttpSession} to provide remember-me service capabilities. + * + * @author Vedran Pavic + * @since 1.3.0 + */ +public class SpringSessionRememberMeServices implements RememberMeServices { + + /** + * Remember-me login request attribute name. + */ + public static final String REMEMBER_ME_LOGIN_ATTR = SpringSessionRememberMeServices.class + .getName() + "REMEMBER_ME_LOGIN_ATTR"; + + private static final String DEFAULT_PARAMETER = "remember-me"; + + private static final int THIRTY_DAYS_SECONDS = 2592000; + + private static final Log logger = LogFactory.getLog(SpringSessionRememberMeServices.class); + + private String parameter = DEFAULT_PARAMETER; + + private boolean alwaysRemember; + + private int validitySeconds = THIRTY_DAYS_SECONDS; + + public final Authentication autoLogin(HttpServletRequest request, + HttpServletResponse response) { + return null; + } + + public final void loginFail(HttpServletRequest request, HttpServletResponse response) { + logger.debug("Interactive login attempt was unsuccessful."); + HttpSession session = request.getSession(false); + if (session != null) { + session.invalidate(); + } + } + + public final void loginSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication successfulAuthentication) { + if (!this.alwaysRemember && !rememberMeRequested(request, this.parameter)) { + logger.debug("Remember-me login not requested."); + return; + } + request.setAttribute(REMEMBER_ME_LOGIN_ATTR, true); + request.getSession().setMaxInactiveInterval(this.validitySeconds); + } + + /** + * Allows customization of whether a remember-me login has been requested. The default + * is to return {@code true} if the configured parameter name has been included in the + * request and is set to the value {@code true}. + * @param request the request submitted from an interactive login, which may include + * additional information indicating that a persistent login is desired. + * @param parameter the configured remember-me parameter name. + * @return true if the request includes information indicating that a persistent login + * has been requested. + */ + protected boolean rememberMeRequested(HttpServletRequest request, String parameter) { + String rememberMe = request.getParameter(parameter); + if (rememberMe != null) { + if (rememberMe.equalsIgnoreCase("true") || rememberMe.equalsIgnoreCase("on") + || rememberMe.equalsIgnoreCase("yes") || rememberMe.equals("1")) { + return true; + } + } + if (logger.isDebugEnabled()) { + logger.debug("Did not send remember-me cookie (principal did not set " + + "parameter '" + parameter + "')"); + } + return false; + } + + /** + * Set the name of the parameter which should be checked for to see if a remember-me + * has been requested during a login request. This should be the same name you assign + * to the checkbox in your login form. + * @param parameter the request parameter + */ + public void setParameter(String parameter) { + Assert.hasText(parameter, "Parameter name cannot be empty or null"); + this.parameter = parameter; + } + + public void setAlwaysRemember(boolean alwaysRemember) { + this.alwaysRemember = alwaysRemember; + } + + public void setValiditySeconds(int validitySeconds) { + this.validitySeconds = validitySeconds; + } + +} diff --git a/spring-session/src/main/java/org/springframework/session/web/http/DefaultCookieSerializer.java b/spring-session/src/main/java/org/springframework/session/web/http/DefaultCookieSerializer.java index b8e50b66..ba34b0bf 100644 --- a/spring-session/src/main/java/org/springframework/session/web/http/DefaultCookieSerializer.java +++ b/spring-session/src/main/java/org/springframework/session/web/http/DefaultCookieSerializer.java @@ -53,6 +53,10 @@ public class DefaultCookieSerializer implements CookieSerializer { private boolean useBase64Encoding; + private String rememberMeRequestAttribute; + + private int rememberMeCookieMaxAge = Integer.MAX_VALUE; + /* * (non-Javadoc) * @@ -111,6 +115,10 @@ public class DefaultCookieSerializer implements CookieSerializer { if ("".equals(requestedCookieValue)) { sessionCookie.setMaxAge(0); } + else if (this.rememberMeRequestAttribute != null && + request.getAttribute(this.rememberMeRequestAttribute) != null) { + sessionCookie.setMaxAge(this.rememberMeCookieMaxAge); + } else { sessionCookie.setMaxAge(this.cookieMaxAge); } @@ -201,6 +209,10 @@ public class DefaultCookieSerializer implements CookieSerializer { * @param cookieMaxAge the maxAge property of the Cookie */ public void setCookieMaxAge(int cookieMaxAge) { + if (cookieMaxAge > this.rememberMeCookieMaxAge) { + throw new IllegalArgumentException("cookieMaxAge cannot be greater than " + + "rememberMeCookieMaxAge"); + } this.cookieMaxAge = cookieMaxAge; } @@ -291,6 +303,36 @@ public class DefaultCookieSerializer implements CookieSerializer { this.useBase64Encoding = useBase64Encoding; } + /** + * Set the request attribute name that indicates remember-me login. Used to write + * {@link Cookie} with {@code maxAge} property indicated by + * {@link #rememberMeCookieMaxAge}. + * @param rememberMeRequestAttribute the remember-me request attribute name + * @since 1.3.0 + * @see #setRememberMeCookieMaxAge(int) + */ + public void setRememberMeRequestAttribute(String rememberMeRequestAttribute) { + if (rememberMeRequestAttribute == null) { + throw new IllegalArgumentException( + "rememberMeRequestAttribute cannot be null"); + } + this.rememberMeRequestAttribute = rememberMeRequestAttribute; + } + + /** + * Set the {@code maxAge} property of the {@link Cookie} to be used when remember-me + * is requested. The default is {@link Integer#MAX_VALUE}. + * @param rememberMeCookieMaxAge the {@code maxAge} property of the {@code Cookie} + * @since 1.3.0 + */ + public void setRememberMeCookieMaxAge(int rememberMeCookieMaxAge) { + if (rememberMeCookieMaxAge < 1 || rememberMeCookieMaxAge < this.cookieMaxAge) { + throw new IllegalArgumentException("rememberMeCookieMaxAge must be greater " + + "than zero and greater than cookieMaxAge"); + } + this.rememberMeCookieMaxAge = rememberMeCookieMaxAge; + } + private String getDomainName(HttpServletRequest request) { if (this.domainName != null) { return this.domainName; diff --git a/spring-session/src/test/java/org/springframework/session/config/annotation/web/http/SpringHttpSessionConfigurationTests.java b/spring-session/src/test/java/org/springframework/session/config/annotation/web/http/SpringHttpSessionConfigurationTests.java new file mode 100644 index 00000000..18b1a003 --- /dev/null +++ b/spring-session/src/test/java/org/springframework/session/config/annotation/web/http/SpringHttpSessionConfigurationTests.java @@ -0,0 +1,135 @@ +/* + * Copyright 2014-2016 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.session.config.annotation.web.http; + +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import org.springframework.beans.factory.UnsatisfiedDependencyException; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.session.MapSessionRepository; +import org.springframework.session.SessionRepository; +import org.springframework.session.security.SpringSessionRememberMeServices; +import org.springframework.session.web.http.CookieHttpSessionStrategy; +import org.springframework.session.web.http.DefaultCookieSerializer; +import org.springframework.session.web.http.SessionEventHttpSessionListenerAdapter; +import org.springframework.session.web.http.SessionRepositoryFilter; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link SpringHttpSessionConfiguration}. + * + * @author Vedran Pavic + */ +@RunWith(MockitoJUnitRunner.class) +public class SpringHttpSessionConfigurationTests { + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + + private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + + @After + public void closeContext() { + if (this.context != null) { + this.context.close(); + } + } + + private void registerAndRefresh(Class... annotatedClasses) { + this.context.register(annotatedClasses); + this.context.refresh(); + } + + @Test + public void noSessionRepositoryConfiguration() { + this.thrown.expect(UnsatisfiedDependencyException.class); + this.thrown.expectMessage("org.springframework.session.SessionRepository"); + + registerAndRefresh(EmptyConfiguration.class); + } + + @Test + public void defaultConfiguration() { + registerAndRefresh(DefaultConfiguration.class); + + assertThat(this.context.getBean(SessionEventHttpSessionListenerAdapter.class)) + .isNotNull(); + assertThat(this.context.getBean(SessionRepositoryFilter.class)).isNotNull(); + assertThat(this.context.getBean(SessionRepository.class)).isNotNull(); + } + + @Test + public void rememberMeServicesConfiguration() { + registerAndRefresh(RememberMeServicesConfiguration.class); + + SessionRepositoryFilter sessionRepositoryFilter = this.context.getBean( + SessionRepositoryFilter.class); + assertThat(sessionRepositoryFilter).isNotNull(); + CookieHttpSessionStrategy httpSessionStrategy = + (CookieHttpSessionStrategy) ReflectionTestUtils.getField( + sessionRepositoryFilter, "httpSessionStrategy"); + assertThat(httpSessionStrategy).isNotNull(); + DefaultCookieSerializer cookieSerializer = + (DefaultCookieSerializer) ReflectionTestUtils.getField( + httpSessionStrategy, "cookieSerializer"); + assertThat(cookieSerializer).isNotNull(); + assertThat(ReflectionTestUtils.getField( + cookieSerializer, "rememberMeRequestAttribute")) + .isEqualTo(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR); + } + + @Configuration + @EnableSpringHttpSession + static class EmptyConfiguration { + } + + static class BaseConfiguration { + + @Bean + public MapSessionRepository sessionRepository() { + return new MapSessionRepository(); + } + + } + + @Configuration + @EnableSpringHttpSession + static class DefaultConfiguration extends BaseConfiguration { + } + + + @Configuration + @EnableSpringHttpSession + static class RememberMeServicesConfiguration extends BaseConfiguration { + + @Bean + public SpringSessionRememberMeServices rememberMeServices() { + return new SpringSessionRememberMeServices(); + } + + } + +} diff --git a/spring-session/src/test/java/org/springframework/session/security/SpringSessionRememberMeServicesTests.java b/spring-session/src/test/java/org/springframework/session/security/SpringSessionRememberMeServicesTests.java new file mode 100644 index 00000000..1afe1c7d --- /dev/null +++ b/spring-session/src/test/java/org/springframework/session/security/SpringSessionRememberMeServicesTests.java @@ -0,0 +1,189 @@ +/* + * Copyright 2014-2016 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.session.security; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.security.core.Authentication; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Tests for {@link SpringSessionRememberMeServices}. + * + * @author Vedran Pavic + */ +public class SpringSessionRememberMeServicesTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private SpringSessionRememberMeServices rememberMeServices; + + @Test + public void create() { + this.rememberMeServices = new SpringSessionRememberMeServices(); + assertThat(ReflectionTestUtils.getField(this.rememberMeServices, "parameter")) + .isEqualTo("remember-me"); + assertThat(ReflectionTestUtils.getField(this.rememberMeServices, "alwaysRemember")) + .isEqualTo(false); + assertThat(ReflectionTestUtils.getField(this.rememberMeServices, "validitySeconds")) + .isEqualTo(2592000); + } + + @Test + public void createWithCustomParameter() { + this.rememberMeServices = new SpringSessionRememberMeServices(); + this.rememberMeServices.setParameter("test-param"); + assertThat(ReflectionTestUtils.getField(this.rememberMeServices, "parameter")) + .isEqualTo("test-param"); + } + + @Test + public void createWithNullParameter() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Parameter name cannot be empty or null"); + this.rememberMeServices = new SpringSessionRememberMeServices(); + this.rememberMeServices.setParameter(null); + } + + @Test + public void createWithAlwaysRemember() { + this.rememberMeServices = new SpringSessionRememberMeServices(); + this.rememberMeServices.setAlwaysRemember(true); + assertThat(ReflectionTestUtils.getField(this.rememberMeServices, "alwaysRemember")) + .isEqualTo(true); + } + + @Test + public void createWithCustomValidity() { + this.rememberMeServices = new SpringSessionRememberMeServices(); + this.rememberMeServices.setValiditySeconds(100000); + assertThat(ReflectionTestUtils.getField(this.rememberMeServices, "validitySeconds")) + .isEqualTo(100000); + } + + @Test + public void autoLogin() { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + this.rememberMeServices = new SpringSessionRememberMeServices(); + this.rememberMeServices.autoLogin(request, response); + verifyZeroInteractions(request, response); + } + + @Test + public void loginFailInvalidatesSession() { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + HttpSession session = mock(HttpSession.class); + given(request.getSession(eq(false))).willReturn(session); + this.rememberMeServices = new SpringSessionRememberMeServices(); + this.rememberMeServices.loginFail(request, response); + verify(request, times(1)).getSession(eq(false)); + verify(session, times(1)).invalidate(); + verifyZeroInteractions(request, response, session); + } + + @Test + public void loginSuccess() { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + Authentication authentication = mock(Authentication.class); + HttpSession session = mock(HttpSession.class); + given(request.getParameter(eq("remember-me"))).willReturn("true"); + given(request.getSession()).willReturn(session); + this.rememberMeServices = new SpringSessionRememberMeServices(); + this.rememberMeServices.loginSuccess(request, response, authentication); + verify(request, times(1)).getParameter(eq("remember-me")); + verify(request, times(1)).getSession(); + verify(request, times(1)).setAttribute( + eq(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR), eq(true)); + verify(session, times(1)).setMaxInactiveInterval(eq(2592000)); + verifyZeroInteractions(request, response, session, authentication); + } + + @Test + public void loginSuccessWithCustomParameter() { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + Authentication authentication = mock(Authentication.class); + HttpSession session = mock(HttpSession.class); + given(request.getParameter(eq("test-param"))).willReturn("true"); + given(request.getSession()).willReturn(session); + this.rememberMeServices = new SpringSessionRememberMeServices(); + this.rememberMeServices.setParameter("test-param"); + this.rememberMeServices.loginSuccess(request, response, authentication); + verify(request, times(1)).getParameter(eq("test-param")); + verify(request, times(1)).getSession(); + verify(request, times(1)).setAttribute( + eq(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR), eq(true)); + verify(session, times(1)).setMaxInactiveInterval(eq(2592000)); + verifyZeroInteractions(request, response, session, authentication); + } + + @Test + public void loginSuccessWithAlwaysRemember() { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + Authentication authentication = mock(Authentication.class); + HttpSession session = mock(HttpSession.class); + given(request.getSession()).willReturn(session); + this.rememberMeServices = new SpringSessionRememberMeServices(); + this.rememberMeServices.setAlwaysRemember(true); + this.rememberMeServices.loginSuccess(request, response, authentication); + verify(request, times(1)).getSession(); + verify(request, times(1)).setAttribute( + eq(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR), eq(true)); + verify(session, times(1)).setMaxInactiveInterval(eq(2592000)); + verifyZeroInteractions(request, response, session, authentication); + } + + @Test + public void loginSuccessWithCustomValidity() { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpServletResponse response = mock(HttpServletResponse.class); + Authentication authentication = mock(Authentication.class); + HttpSession session = mock(HttpSession.class); + given(request.getParameter(eq("remember-me"))) + .willReturn("true"); + given(request.getSession()).willReturn(session); + this.rememberMeServices = new SpringSessionRememberMeServices(); + this.rememberMeServices.setValiditySeconds(100000); + this.rememberMeServices.loginSuccess(request, response, authentication); + verify(request, times(1)).getParameter(eq("remember-me")); + verify(request, times(1)).getSession(); + verify(request, times(1)).setAttribute( + eq(SpringSessionRememberMeServices.REMEMBER_ME_LOGIN_ATTR), eq(true)); + verify(session, times(1)).setMaxInactiveInterval(eq(100000)); + verifyZeroInteractions(request, response, session, authentication); + } + +} diff --git a/spring-session/src/test/java/org/springframework/session/web/http/DefaultCookieSerializerTests.java b/spring-session/src/test/java/org/springframework/session/web/http/DefaultCookieSerializerTests.java index bce0a1c3..28ff3bf0 100644 --- a/spring-session/src/test/java/org/springframework/session/web/http/DefaultCookieSerializerTests.java +++ b/spring-session/src/test/java/org/springframework/session/web/http/DefaultCookieSerializerTests.java @@ -19,7 +19,9 @@ package org.springframework.session.web.http; import javax.servlet.http.Cookie; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @@ -45,6 +47,9 @@ public class DefaultCookieSerializerTests { return new Object[] { false, true }; } + @Rule + public ExpectedException thrown = ExpectedException.none(); + private boolean useBase64Encoding; private String cookieName; @@ -424,6 +429,45 @@ public class DefaultCookieSerializerTests { assertThat(this.serializer.readCookieValues(this.request)).containsOnly(""); } + // --- rememberMe --- + + @Test + public void setRememberMeCookieMaxAgeNotGreaterThanZero() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("rememberMeCookieMaxAge must be greater than zero and " + + "greater than cookieMaxAge"); + this.serializer.setRememberMeCookieMaxAge(0); + } + + @Test + public void setRememberMeCookieMaxAgeNotGreaterMaxCookieAge() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("rememberMeCookieMaxAge must be greater than zero and " + + "greater than cookieMaxAge"); + this.serializer.setCookieMaxAge(20); + this.serializer.setRememberMeCookieMaxAge(10); + } + + @Test + public void writeCookieRememberMeCookieMaxAgeDefault() { + this.request.setAttribute("rememberMe", true); + this.serializer.setRememberMeRequestAttribute("rememberMe"); + this.serializer.writeCookieValue(cookieValue(this.sessionId)); + + assertThat(getCookie().getMaxAge()).isEqualTo(Integer.MAX_VALUE); + } + + @Test + public void writeCookieRememberMeCookieMaxAgeExplicit() { + this.request.setAttribute("rememberMe", true); + this.serializer.setRememberMeRequestAttribute("rememberMe"); + this.serializer.setRememberMeCookieMaxAge(100); + + this.serializer.writeCookieValue(cookieValue(this.sessionId)); + + assertThat(getCookie().getMaxAge()).isEqualTo(100); + } + public void setCookieName(String cookieName) { this.cookieName = cookieName; this.serializer.setCookieName(cookieName);