Prechádzať zdrojové kódy

NamespaceRememberMeTests groovy->java

Issue gh-4939
Josh Cummings 6 rokov pred
rodič
commit
d6d0d89ff8

+ 0 - 391
config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.groovy

@@ -1,391 +0,0 @@
-/*
- * Copyright 2002-2015 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
- *
- *      https://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.security.config.annotation.web.configurers
-
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.BaseWebConfig;
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
-import org.springframework.security.web.authentication.logout.LogoutHandler;
-
-import javax.servlet.http.Cookie
-
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration
-import org.springframework.mock.web.MockFilterChain
-import org.springframework.mock.web.MockHttpServletRequest;
-import org.springframework.mock.web.MockHttpServletResponse
-import org.springframework.mock.web.MockHttpSession
-import org.springframework.security.authentication.AuthenticationManager
-import org.springframework.security.authentication.RememberMeAuthenticationToken;
-import org.springframework.security.config.annotation.BaseSpringSpec
-import org.springframework.security.core.authority.AuthorityUtils;
-import org.springframework.security.core.context.SecurityContext
-import org.springframework.security.core.userdetails.UserDetailsService
-import org.springframework.security.web.FilterChainProxy
-import org.springframework.security.web.authentication.AuthenticationSuccessHandler
-import org.springframework.security.web.authentication.RememberMeServices
-import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
-import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices
-import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
-import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
-import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter
-import org.springframework.security.web.context.HttpRequestResponseHolder
-import org.springframework.security.web.context.HttpSessionSecurityContextRepository
-import org.springframework.test.util.ReflectionTestUtils;
-
-/**
- * Tests to verify that all the functionality of <anonymous> attributes is present
- *
- * @author Rob Winch
- *
- */
-public class NamespaceRememberMeTests extends BaseSpringSpec {
-
-	def "http/remember-me"() {
-		setup:
-			loadConfig(RememberMeConfig)
-		when: "login with remember me"
-			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
-		when: "session expires"
-			super.setup()
-			request.setCookies(rememberMeCookie)
-			request.requestURI = "/abc"
-			springSecurityFilterChain.doFilter(request,response,chain)
-			MockHttpSession session = request.getSession()
-		then: "initialized to RememberMeAuthenticationToken"
-			SecurityContext context = new HttpSessionSecurityContextRepository().loadContext(new HttpRequestResponseHolder(request, response))
-			context.getAuthentication() instanceof RememberMeAuthenticationToken
-		when: "logout"
-			super.setup()
-			request.setSession(session)
-			super.setupCsrf()
-			request.setCookies(rememberMeCookie)
-			request.servletPath = "/logout"
-			request.method = "POST"
-			springSecurityFilterChain.doFilter(request,response,chain)
-			rememberMeCookie = getRememberMeCookie()
-		then: "logout cookie expired"
-			response.getRedirectedUrl() == "/login?logout"
-			rememberMeCookie.maxAge == 0
-		when: "use remember me after logout"
-			super.setup()
-			request.setCookies(rememberMeCookie)
-			request.requestURI = "/abc"
-			springSecurityFilterChain.doFilter(request,response,chain)
-		then: "sent to default login page"
-			response.getRedirectedUrl() == "http://localhost/login"
-	}
-
-	@Configuration
-	static class RememberMeConfig extends BaseWebConfig {
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.authorizeRequests()
-					.anyRequest().hasRole("USER")
-					.and()
-				.formLogin()
-					.and()
-				.rememberMe()
-		}
-	}
-
-	// See SEC-3170
-	static interface RememberMeServicesLogoutHandler extends RememberMeServices, LogoutHandler{}
-
-	def "http/remember-me@services-ref"() {
-		setup:
-			RememberMeServicesRefConfig.REMEMBER_ME_SERVICES = Mock(RememberMeServicesLogoutHandler)
-		when: "use custom remember-me services"
-			loadConfig(RememberMeServicesRefConfig)
-		then: "custom remember-me services used"
-			findFilter(RememberMeAuthenticationFilter).rememberMeServices == RememberMeServicesRefConfig.REMEMBER_ME_SERVICES
-			findFilter(UsernamePasswordAuthenticationFilter).rememberMeServices == RememberMeServicesRefConfig.REMEMBER_ME_SERVICES
-	}
-
-	@Configuration
-	static class RememberMeServicesRefConfig extends BaseWebConfig {
-		static RememberMeServices REMEMBER_ME_SERVICES
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.formLogin()
-					.and()
-				.rememberMe()
-					.rememberMeServices(REMEMBER_ME_SERVICES)
-		}
-	}
-
-	def "http/remember-me@authentication-success-handler-ref"() {
-		setup:
-			AuthSuccessConfig.SUCCESS_HANDLER = Mock(AuthenticationSuccessHandler)
-		when: "use custom success handler"
-			loadConfig(AuthSuccessConfig)
-		then: "custom remember-me success handler is used"
-			findFilter(RememberMeAuthenticationFilter).successHandler == AuthSuccessConfig.SUCCESS_HANDLER
-	}
-
-	@Configuration
-	static class AuthSuccessConfig extends BaseWebConfig {
-		static AuthenticationSuccessHandler SUCCESS_HANDLER
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.formLogin()
-					.and()
-				.rememberMe()
-					.authenticationSuccessHandler(SUCCESS_HANDLER)
-		}
-	}
-
-	// http/remember-me@data-source-ref is not supported directly. Instead use http/remember-me@token-repository-ref example
-
-	def "http/remember-me@key"() {
-		when: "use custom key"
-			loadConfig(KeyConfig)
-			AuthenticationManager authManager = context.getBean(AuthenticationManager)
-		then: "custom key services used"
-			findFilter(RememberMeAuthenticationFilter).rememberMeServices.key == "KeyConfig"
-			authManager.authenticate(new RememberMeAuthenticationToken("KeyConfig", "user", AuthorityUtils.createAuthorityList("ROLE_USER")))
-	}
-
-	@Configuration
-	static class KeyConfig extends BaseWebConfig {
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.formLogin()
-					.and()
-				.rememberMe()
-					.key("KeyConfig")
-		}
-
-		@Bean
-		@Override
-		public AuthenticationManager authenticationManagerBean()
-				throws Exception {
-			return super.authenticationManagerBean();
-		}
-	}
-
-	// http/remember-me@services-alias is not supported use standard aliasing instead (i.e. @Bean("alias"))
-
-	def "http/remember-me@token-repository-ref"() {
-		setup:
-			TokenRepositoryRefConfig.TOKEN_REPOSITORY = Mock(PersistentTokenRepository)
-		when: "use custom token services"
-			loadConfig(TokenRepositoryRefConfig)
-		then: "custom token services used with PersistentTokenBasedRememberMeServices"
-			PersistentTokenBasedRememberMeServices rememberMeServices = findFilter(RememberMeAuthenticationFilter).rememberMeServices
-			findFilter(RememberMeAuthenticationFilter).rememberMeServices.tokenRepository == TokenRepositoryRefConfig.TOKEN_REPOSITORY
-	}
-
-	@Configuration
-	static class TokenRepositoryRefConfig extends BaseWebConfig {
-		static PersistentTokenRepository TOKEN_REPOSITORY
-		protected void configure(HttpSecurity http) throws Exception {
-			// JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl()
-			// tokenRepository.setDataSource(dataSource);
-			http
-				.formLogin()
-					.and()
-				.rememberMe()
-					.tokenRepository(TOKEN_REPOSITORY)
-		}
-	}
-
-	def "http/remember-me@token-validity-seconds"() {
-		when: "use token validity"
-			loadConfig(TokenValiditySecondsConfig)
-		then: "custom token validity used"
-			findFilter(RememberMeAuthenticationFilter).rememberMeServices.tokenValiditySeconds == 1
-	}
-
-	@Configuration
-	static class TokenValiditySecondsConfig extends BaseWebConfig {
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.formLogin()
-					.and()
-				.rememberMe()
-					.tokenValiditySeconds(1)
-		}
-	}
-
-	def "http/remember-me@token-validity-seconds default"() {
-		when: "use token validity"
-			loadConfig(DefaultTokenValiditySecondsConfig)
-		then: "custom token validity used"
-			findFilter(RememberMeAuthenticationFilter).rememberMeServices.tokenValiditySeconds == AbstractRememberMeServices.TWO_WEEKS_S
-	}
-
-	@Configuration
-	static class DefaultTokenValiditySecondsConfig extends BaseWebConfig {
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.formLogin()
-					.and()
-				.rememberMe()
-		}
-	}
-
-	def "http/remember-me@use-secure-cookie"() {
-		when: "use secure cookies = true"
-			loadConfig(UseSecureCookieConfig)
-		then: "secure cookies will be used"
-			ReflectionTestUtils.getField(findFilter(RememberMeAuthenticationFilter).rememberMeServices, "useSecureCookie") == true
-	}
-
-	@Configuration
-	static class UseSecureCookieConfig extends BaseWebConfig {
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.formLogin()
-					.and()
-				.rememberMe()
-					.useSecureCookie(true)
-		}
-	}
-
-	def "http/remember-me@remember-me-parameter"() {
-		when: "use custom rememberMeParameter"
-			loadConfig(RememberMeParameterConfig)
-		then: "custom rememberMeParameter will be used"
-			findFilter(RememberMeAuthenticationFilter).rememberMeServices.parameter == "rememberMe"
-	}
-
-	@Configuration
-	static class RememberMeParameterConfig extends BaseWebConfig {
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.formLogin()
-					.and()
-				.rememberMe()
-					.rememberMeParameter("rememberMe")
-		}
-	}
-
-	// SEC-2880
-	def "http/remember-me@remember-me-cookie"() {
-		when: "use custom rememberMeCookieName"
-		loadConfig(RememberMeCookieNameConfig)
-		then: "custom rememberMeCookieName will be used"
-		findFilter(RememberMeAuthenticationFilter).rememberMeServices.cookieName == "rememberMe"
-	}
-
-	@Configuration
-	static class RememberMeCookieNameConfig extends BaseWebConfig {
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.formLogin()
-					.and()
-				.rememberMe()
-					.rememberMeCookieName("rememberMe")
-		}
-	}
-
-	def "http/remember-me@use-secure-cookie defaults"() {
-		when: "use secure cookies not specified"
-			loadConfig(DefaultUseSecureCookieConfig)
-		then: "secure cookies will be null (use secure if the request is secure)"
-			ReflectionTestUtils.getField(findFilter(RememberMeAuthenticationFilter).rememberMeServices, "useSecureCookie") == null
-	}
-
-	@Configuration
-	static class DefaultUseSecureCookieConfig extends BaseWebConfig {
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.formLogin()
-					.and()
-				.rememberMe()
-		}
-	}
-
-	def "http/remember-me defaults UserDetailsService with custom UserDetailsService"() {
-		setup:
-			DefaultsUserDetailsServiceWithDaoConfig.USERDETAILS_SERVICE = Mock(UserDetailsService)
-			loadConfig(DefaultsUserDetailsServiceWithDaoConfig)
-		when:
-			request.setCookies(createRememberMeCookie())
-			springSecurityFilterChain.doFilter(request, response, chain)
-		then: "RememberMeServices defaults to the custom UserDetailsService"
-		   1 * DefaultsUserDetailsServiceWithDaoConfig.USERDETAILS_SERVICE.loadUserByUsername("user")
-	}
-
-	@EnableWebSecurity
-	static class DefaultsUserDetailsServiceWithDaoConfig extends WebSecurityConfigurerAdapter {
-		static UserDetailsService USERDETAILS_SERVICE
-
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.formLogin()
-					.and()
-				.rememberMe()
-		}
-
-		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
-			auth
-					.userDetailsService(USERDETAILS_SERVICE);
-		}
-	}
-
-	def "http/remember-me@user-service-ref"() {
-		setup:
-			UserServiceRefConfig.USERDETAILS_SERVICE = Mock(UserDetailsService)
-		when: "use custom UserDetailsService"
-			loadConfig(UserServiceRefConfig)
-		then: "custom UserDetailsService is used"
-			ReflectionTestUtils.getField(findFilter(RememberMeAuthenticationFilter).rememberMeServices, "userDetailsService") == UserServiceRefConfig.USERDETAILS_SERVICE
-	}
-
-	@Configuration
-	static class UserServiceRefConfig extends BaseWebConfig {
-		static UserDetailsService USERDETAILS_SERVICE
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-				.formLogin()
-					.and()
-				.rememberMe()
-					.userDetailsService(USERDETAILS_SERVICE)
-		}
-	}
-
-	Cookie createRememberMeCookie() {
-		MockHttpServletRequest request = new MockHttpServletRequest("GET", "")
-		MockHttpServletResponse response = new MockHttpServletResponse()
-		super.setupCsrf("CSRF_TOKEN", request, response)
-
-		MockFilterChain chain = new MockFilterChain()
-		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)
-		response.getCookie("remember-me")
-	}
-
-	Cookie getRememberMeCookie(String cookieName="remember-me") {
-		response.getCookie(cookieName)
-	}
-}

+ 515 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.java

@@ -0,0 +1,515 @@
+/*
+ * Copyright 2002-2019 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
+ *
+ *      https://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.security.config.annotation.web.configurers;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.mock.web.MockHttpSession;
+import org.springframework.security.authentication.RememberMeAuthenticationToken;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+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;
+import org.springframework.security.config.test.SpringTestRule;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.security.web.authentication.RememberMeServices;
+import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
+import org.springframework.security.web.authentication.rememberme.PersistentRememberMeToken;
+import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.request.RequestPostProcessor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+/**
+ * Tests to verify that all the functionality of <anonymous> attributes is present
+ *
+ * @author Rob Winch
+ * @author Josh Cummings
+ *
+ */
+public class NamespaceRememberMeTests {
+
+	@Rule
+	public final SpringTestRule spring = new SpringTestRule();
+
+	@Autowired
+	MockMvc mvc;
+
+	@Test
+	public void rememberMeLoginWhenUsingDefaultsThenMatchesNamespace() throws Exception {
+		this.spring.register(RememberMeConfig.class, SecurityController.class).autowire();
+		MvcResult result = this.mvc.perform(post("/login")
+				.with(rememberMeLogin()))
+				.andReturn();
+
+		MockHttpSession session = (MockHttpSession) result.getRequest().getSession();
+		Cookie rememberMe = result.getResponse().getCookie("remember-me");
+		assertThat(rememberMe).isNotNull();
+		this.mvc.perform(get("/authentication-class")
+				.cookie(rememberMe))
+				.andExpect(content().string(RememberMeAuthenticationToken.class.getName()));
+
+		result = this.mvc.perform(post("/logout").with(csrf())
+				.session(session)
+				.cookie(rememberMe))
+				.andExpect(redirectedUrl("/login?logout"))
+				.andReturn();
+
+		rememberMe = result.getResponse().getCookie("remember-me");
+		assertThat(rememberMe).isNotNull().extracting("maxAge").containsExactly(0);
+
+		this.mvc.perform(post("/authentication-class").with(csrf())
+				.cookie(rememberMe))
+				.andExpect(redirectedUrl("http://localhost/login"))
+				.andReturn();
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	static class RememberMeConfig extends UsersConfig {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeRequests()
+					.anyRequest().hasRole("USER")
+					.and()
+				.formLogin()
+					.and()
+				.rememberMe();
+			// @formatter:on
+		}
+	}
+
+	// SEC-3170 - RememberMeService implementations should not have to also implement LogoutHandler
+	@Test
+	public void logoutWhenCustomRememberMeServicesDeclaredThenUses() throws Exception {
+		RememberMeServicesRefConfig.REMEMBER_ME_SERVICES = mock(RememberMeServicesWithoutLogoutHandler.class);
+		this.spring.register(RememberMeServicesRefConfig.class).autowire();
+
+		this.mvc.perform(get("/"));
+		verify(RememberMeServicesRefConfig.REMEMBER_ME_SERVICES)
+				.autoLogin(any(HttpServletRequest.class), any(HttpServletResponse.class));
+
+		this.mvc.perform(post("/login").with(csrf()));
+		verify(RememberMeServicesRefConfig.REMEMBER_ME_SERVICES)
+				.loginFail(any(HttpServletRequest.class), any(HttpServletResponse.class));
+	}
+
+	interface RememberMeServicesWithoutLogoutHandler extends RememberMeServices {}
+
+	@Configuration
+	@EnableWebSecurity
+	static class RememberMeServicesRefConfig extends WebSecurityConfigurerAdapter {
+		static RememberMeServices REMEMBER_ME_SERVICES;
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.formLogin()
+					.and()
+				.rememberMe()
+					.rememberMeServices(REMEMBER_ME_SERVICES);
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void rememberMeLoginWhenAuthenticationSuccessHandlerDeclaredThenUses() throws Exception {
+		AuthSuccessConfig.SUCCESS_HANDLER = mock(AuthenticationSuccessHandler.class);
+		this.spring.register(AuthSuccessConfig.class).autowire();
+
+		MvcResult result = this.mvc.perform(post("/login")
+				.with(rememberMeLogin()))
+				.andReturn();
+
+		verifyZeroInteractions(AuthSuccessConfig.SUCCESS_HANDLER);
+
+		Cookie rememberMe = result.getResponse().getCookie("remember-me");
+		assertThat(rememberMe).isNotNull();
+		this.mvc.perform(get("/somewhere")
+				.cookie(rememberMe));
+
+		verify(AuthSuccessConfig.SUCCESS_HANDLER).onAuthenticationSuccess
+				(any(HttpServletRequest.class), any(HttpServletResponse.class), any(Authentication.class));
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	static class AuthSuccessConfig extends UsersConfig {
+		static AuthenticationSuccessHandler SUCCESS_HANDLER;
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.formLogin()
+					.and()
+				.rememberMe()
+					.authenticationSuccessHandler(SUCCESS_HANDLER);
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void rememberMeLoginWhenKeyDeclaredThenMatchesNamespace() throws Exception {
+		this.spring.register(WithoutKeyConfig.class, KeyConfig.class, SecurityController.class).autowire();
+		Cookie withoutKey = this.mvc.perform(post("/without-key/login")
+				.with(rememberMeLogin()))
+				.andExpect(redirectedUrl("/"))
+				.andReturn().getResponse().getCookie("remember-me");
+
+		this.mvc.perform(get("/somewhere")
+				.cookie(withoutKey))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("http://localhost/login"));
+
+		Cookie withKey = this.mvc.perform(post("/login")
+				.with(rememberMeLogin()))
+				.andReturn().getResponse().getCookie("remember-me");
+		this.mvc.perform(get("/somewhere")
+				.cookie(withKey))
+				.andExpect(status().isNotFound());
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	@Order(0)
+	static class WithoutKeyConfig extends UsersConfig {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.antMatcher("/without-key/**")
+					.formLogin()
+						.loginProcessingUrl("/without-key/login")
+						.and()
+					.rememberMe();
+			// @formatter:on
+		}
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	static class KeyConfig extends UsersConfig {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeRequests()
+					.anyRequest().authenticated()
+					.and()
+				.formLogin()
+					.and()
+				.rememberMe()
+					.key("KeyConfig");
+			// @formatter:on
+		}
+	}
+
+	// http/remember-me@services-alias is not supported use standard aliasing instead (i.e. @Bean("alias"))
+
+	// http/remember-me@data-source-ref is not supported directly. Instead use http/remember-me@token-repository-ref example
+	@Test
+	public void rememberMeLoginWhenDeclaredTokenRepositoryThenMatchesNamespace() throws Exception {
+		TokenRepositoryRefConfig.TOKEN_REPOSITORY = mock(PersistentTokenRepository.class);
+		this.spring.register(TokenRepositoryRefConfig.class).autowire();
+
+		this.mvc.perform(post("/login")
+				.with(rememberMeLogin()));
+
+		verify(TokenRepositoryRefConfig.TOKEN_REPOSITORY).createNewToken(any(PersistentRememberMeToken.class));
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	static class TokenRepositoryRefConfig extends UsersConfig {
+		static PersistentTokenRepository TOKEN_REPOSITORY;
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl()
+			// tokenRepository.setDataSource(dataSource);
+
+			// @formatter:off
+			http
+				.formLogin()
+					.and()
+				.rememberMe()
+					.tokenRepository(TOKEN_REPOSITORY);
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void rememberMeLoginWhenTokenValidityDeclaredThenMatchesNamespace() throws Exception {
+		this.spring.register(TokenValiditySecondsConfig.class).autowire();
+		Cookie expiredRememberMe = this.mvc.perform(post("/login")
+				.with(rememberMeLogin()))
+				.andReturn().getResponse().getCookie("remember-me");
+
+		assertThat(expiredRememberMe).extracting("maxAge").containsExactly(314);
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	static class TokenValiditySecondsConfig extends UsersConfig {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeRequests()
+					.anyRequest().authenticated()
+					.and()
+				.formLogin()
+					.and()
+				.rememberMe()
+					.tokenValiditySeconds(314);
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void rememberMeLoginWhenUsingDefaultsThenCookieMaxAgeMatchesNamespace() throws Exception {
+		this.spring.register(RememberMeConfig.class).autowire();
+		Cookie expiredRememberMe = this.mvc.perform(post("/login")
+				.with(rememberMeLogin()))
+				.andReturn().getResponse().getCookie("remember-me");
+
+		assertThat(expiredRememberMe).extracting("maxAge")
+				.containsExactly(AbstractRememberMeServices.TWO_WEEKS_S);
+	}
+
+	@Test
+	public void rememberMeLoginWhenUsingSecureCookieThenMatchesNamespace() throws Exception {
+		this.spring.register(UseSecureCookieConfig.class).autowire();
+		Cookie secureCookie = this.mvc.perform(post("/login")
+				.with(rememberMeLogin()))
+				.andReturn().getResponse().getCookie("remember-me");
+
+		assertThat(secureCookie).extracting("secure").containsExactly(true);
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	static class UseSecureCookieConfig extends UsersConfig {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.formLogin()
+					.and()
+				.rememberMe()
+					.useSecureCookie(true);
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void rememberMeLoginWhenUsingDefaultsThenCookieSecurityMatchesNamespace() throws Exception {
+		this.spring.register(RememberMeConfig.class).autowire();
+		Cookie secureCookie = this.mvc.perform(post("/login")
+				.with(rememberMeLogin())
+				.secure(true))
+				.andReturn().getResponse().getCookie("remember-me");
+
+		assertThat(secureCookie).extracting("secure").containsExactly(true);
+	}
+
+	@Test
+	public void rememberMeLoginWhenParameterSpecifiedThenMatchesNamespace() throws Exception {
+		this.spring.register(RememberMeParameterConfig.class).autowire();
+		Cookie rememberMe = this.mvc.perform(post("/login")
+				.with(rememberMeLogin("rememberMe", true)))
+				.andReturn().getResponse().getCookie("remember-me");
+
+		assertThat(rememberMe).isNotNull();
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	static class RememberMeParameterConfig extends UsersConfig {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.formLogin()
+					.and()
+				.rememberMe()
+					.rememberMeParameter("rememberMe");
+			// @formatter:on
+		}
+	}
+
+	// SEC-2880
+
+	@Test
+	public void rememberMeLoginWhenCookieNameDeclaredThenMatchesNamespace() throws Exception {
+		this.spring.register(RememberMeCookieNameConfig.class).autowire();
+		Cookie rememberMe = this.mvc.perform(post("/login")
+				.with(rememberMeLogin()))
+				.andReturn().getResponse().getCookie("rememberMe");
+
+		assertThat(rememberMe).isNotNull();
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	static class RememberMeCookieNameConfig extends UsersConfig {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.formLogin()
+					.and()
+				.rememberMe()
+					.rememberMeCookieName("rememberMe");
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void rememberMeLoginWhenGlobalUserDetailsServiceDeclaredThenMatchesNamespace() throws Exception {
+		DefaultsUserDetailsServiceWithDaoConfig.USERDETAILS_SERVICE = mock(UserDetailsService.class);
+		this.spring.register(DefaultsUserDetailsServiceWithDaoConfig.class).autowire();
+
+		this.mvc.perform(post("/login")
+				.with(rememberMeLogin()));
+
+		verify(DefaultsUserDetailsServiceWithDaoConfig.USERDETAILS_SERVICE)
+				.loadUserByUsername("user");
+	}
+
+	@EnableWebSecurity
+	@Configuration
+	static class DefaultsUserDetailsServiceWithDaoConfig extends WebSecurityConfigurerAdapter {
+		static UserDetailsService USERDETAILS_SERVICE;
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.formLogin()
+					.and()
+				.rememberMe();
+			// @formatter:on
+		}
+
+		@Override
+		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+			auth
+				.userDetailsService(USERDETAILS_SERVICE);
+		}
+	}
+
+	@Test
+	public void rememberMeLoginWhenUserDetailsServiceDeclaredThenMatchesNamespace() throws Exception {
+		UserServiceRefConfig.USERDETAILS_SERVICE = mock(UserDetailsService.class);
+		this.spring.register(UserServiceRefConfig.class).autowire();
+
+		when(UserServiceRefConfig.USERDETAILS_SERVICE.loadUserByUsername("user"))
+				.thenReturn(new User("user", "password", AuthorityUtils.createAuthorityList("ROLE_USER")));
+
+		this.mvc.perform(post("/login")
+				.with(rememberMeLogin()));
+
+		verify(UserServiceRefConfig.USERDETAILS_SERVICE)
+				.loadUserByUsername("user");
+	}
+
+	@Configuration
+	@EnableWebSecurity
+	static class UserServiceRefConfig extends UsersConfig {
+		static UserDetailsService USERDETAILS_SERVICE;
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.formLogin()
+					.and()
+				.rememberMe()
+					.userDetailsService(USERDETAILS_SERVICE);
+			// @formatter:on
+		}
+	}
+
+	static RequestPostProcessor rememberMeLogin() {
+		return rememberMeLogin("remember-me", true);
+	}
+
+	static RequestPostProcessor rememberMeLogin(String parameterName, boolean parameterValue) {
+		return request -> {
+			csrf().postProcessRequest(request);
+			request.setParameter("username", "user");
+			request.setParameter("password", "password");
+			request.setParameter(parameterName, String.valueOf(parameterValue));
+			return request;
+		};
+	}
+
+	static class UsersConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		@Bean
+		public UserDetailsService userDetailsService() {
+			return new InMemoryUserDetailsManager(
+					User.withDefaultPasswordEncoder()
+							.username("user")
+							.password("password")
+							.roles("USER")
+							.build());
+		}
+	}
+
+	@RestController
+	static class SecurityController {
+		@GetMapping("/authentication-class")
+		String authenticationClass(Authentication authentication) {
+			return authentication.getClass().getName();
+		}
+	}
+}