浏览代码

SEC-3094: Add @WithAnonymousUser & anonymous() MockMvcRequestPostProcessor

Rob Winch 10 年之前
父节点
当前提交
35393098f8

+ 41 - 0
docs/manual/src/docs/asciidoc/_includes/test.adoc

@@ -137,6 +137,37 @@ For example, the following would run every test with a user with the username "a
 public class WithMockUserTests {
 ----
 
+
+[[test-method-withanonymoususer]]
+=== @WithAnonymousUser
+
+Using `@WithAnonymousUser` allows running as an anonymous user.
+This is especially convenient when you wish to run most of your tests with a specific user, but want to run a few tests as an anonymous user.
+For example, the following will run withMockUser1 and withMockUser2 using <<test-method-withmockuser,@WithMockUser>> and anonymous as an anonymous user.
+
+[source,java]
+----
+@RunWith(SpringJUnit4ClassRunner.class)
+@WithMockUser
+public class WithUserClassLevelAuthenticationTests {
+
+	@Test
+	public void withMockUser1() {
+	}
+
+	@Test
+	public void withMockUser2() {
+	}
+
+	@Test
+	@WithAnonymousUser
+	public void anonymous() throws Exception {
+		// override default to run as anonymous user
+	}
+}
+----
+
+
 [[test-method-withuserdetails]]
 === @WithUserDetails
 
@@ -409,6 +440,16 @@ mvc
 	.perform(get("/").with(user(userDetails)))
 ----
 
+You can run as anonymous user using the following:
+
+[source,java]
+----
+mvc
+	.perform(get("/").with(anonymous()))
+----
+
+This is especially useful if you are running with a default user and wish to execute a few requests as an anonymous user.
+
 If you want a custom `Authentication` (which does not need to exist) you can do so using the following:
 
 [source,java]

+ 1 - 0
docs/manual/src/docs/asciidoc/index.adoc

@@ -373,6 +373,7 @@ This will give you access to the entire project history (including all releases
 ** <<test-method-meta-annotations,Test Meta Annotations>>
 ** <<method-security-meta-annotations,Method Security Meta Annotations>>
 * <<el-access-web-path-variables,Path Variables in Web Security Expressions>>
+* <<test-method-withanonymoususer,@WithAnonymousUser>>
 
 === What's new in Spring Security 4.0
 

+ 61 - 0
test/src/main/java/org/springframework/security/test/context/support/WithAnonymousUser.java

@@ -0,0 +1,61 @@
+/*
+ * 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
+ *
+ *      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.security.test.context.support;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.context.SecurityContext;
+
+/**
+ * When used with {@link WithSecurityContextTestExecutionListener} this
+ * annotation can be added to a test method to emulate running with an anonymous
+ * user. The {@link SecurityContext} that is used will contain an
+ * {@link AnonymousAuthenticationToken}. This is useful when a user wants to run
+ * a majority of tests as a specific user and wishes to override a few methods
+ * to be anonymous. For example:
+ *
+ * <pre>
+ * <code>
+ * &#064;WithMockUser
+ * public class SecurityTests {
+ *     &#064;Test
+ *     &#064;WithAnonymousUser
+ *     public void runAsAnonymous() {
+ *         // ... run as an anonymous user ...
+ *     }
+ *
+ *     // ... lots of tests ran with a default user ...
+ * }
+ * </code>
+ * </pre>
+ *
+ * @author Rob Winch
+ * @since 4.1
+ */
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+@WithSecurityContext(factory = WithAnonymousUserSecurityContextFactory.class)
+public @interface WithAnonymousUser {
+
+}

+ 47 - 0
test/src/main/java/org/springframework/security/test/context/support/WithAnonymousUserSecurityContextFactory.java

@@ -0,0 +1,47 @@
+/*
+ * Copyright 2002-2014 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.security.test.context.support;
+
+import java.util.List;
+
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+
+/**
+ * A {@link WithAnonymousUserSecurityContextFactory} that runs with an {@link AnonymousAuthenticationToken}.
+ * .
+ *
+ * @see WithUserDetails
+ *
+ * @author Rob Winch
+ * @since 4.1
+ */
+
+final class WithAnonymousUserSecurityContextFactory implements
+		WithSecurityContextFactory<WithAnonymousUser> {
+
+	public SecurityContext createSecurityContext(WithAnonymousUser withUser) {
+		List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS");
+		Authentication authentication = new AnonymousAuthenticationToken("key", "anonymous", authorities);
+		SecurityContext context = SecurityContextHolder.createEmptyContext();
+		context.setAuthentication(authentication);
+		return context;
+	}
+}

+ 44 - 0
test/src/main/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessors.java

@@ -34,6 +34,7 @@ import org.springframework.core.io.Resource;
 import org.springframework.core.io.ResourceLoader;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
@@ -217,6 +218,38 @@ public final class SecurityMockMvcRequestPostProcessors {
 		return new AuthenticationRequestPostProcessor(authentication);
 	}
 
+	/**
+	 * Establish a {@link SecurityContext} that uses an
+	 * {@link AnonymousAuthenticationToken}. This is useful when a user wants to
+	 * run a majority of tests as a specific user and wishes to override a few
+	 * methods to be anonymous. For example:
+	 *
+	 * <pre>
+	 * <code>
+	 * public class SecurityTests {
+	 *     &#064;Before
+	 *     public void setup() {
+	 *         mockMvc = MockMvcBuilders
+	 *             .webAppContextSetup(context)
+	 *             .defaultRequest(get("/").with(user("user")))
+	 *             .build();
+	 *     }
+	 *
+	 *     &#064;Test
+	 *     public void anonymous() {
+	 *         mockMvc.perform(get("anonymous").with(anonymous()));
+	 *     }
+	 *     // ... lots of tests ran with a default user ...
+	 * }
+	 * </code>
+	 * </pre>
+	 *
+	 * @return the {@link RequestPostProcessor} to use
+	 */
+	public static RequestPostProcessor anonymous() {
+		return new AnonymousRequestPostProcessor();
+	}
+
 	/**
 	 * Establish the specified {@link SecurityContext} to be used.
 	 *
@@ -761,6 +794,17 @@ public final class SecurityMockMvcRequestPostProcessors {
 		}
 	}
 
+	private static class AnonymousRequestPostProcessor extends SecurityContextRequestPostProcessorSupport implements RequestPostProcessor {
+		private AuthenticationRequestPostProcessor delegate = new AuthenticationRequestPostProcessor(new AnonymousAuthenticationToken("key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")));
+
+		/* (non-Javadoc)
+		 * @see org.springframework.test.web.servlet.request.RequestPostProcessor#postProcessRequest(org.springframework.mock.web.MockHttpServletRequest)
+		 */
+		public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
+			return delegate.postProcessRequest(request);
+		}
+	}
+
 	private static class HttpBasicRequestPostProcessor implements RequestPostProcessor {
 		private String headerValue;
 

+ 13 - 10
test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/DefaultfSecurityRequestsTests.java

@@ -18,20 +18,17 @@ package org.springframework.security.test.web.servlet.showcase.secured;
 import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
 import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;
 import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
-
-import javax.servlet.Filter;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Configuration;
 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.WebSecurityConfigurerAdapter;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 import org.springframework.test.context.web.WebAppConfiguration;
@@ -48,9 +45,6 @@ public class DefaultfSecurityRequestsTests {
 	@Autowired
 	private WebApplicationContext context;
 
-	@Autowired
-	private Filter springSecurityFilterChain;
-
 	private MockMvc mvc;
 
 	@Before
@@ -78,6 +72,15 @@ public class DefaultfSecurityRequestsTests {
 				.andExpect(authenticated().withUsername("user"));
 	}
 
+	@Test
+	public void requestProtectedUrlWithAnonymous() throws Exception {
+		mvc.perform(get("/admin").with(anonymous()))
+				// Ensure we got past Security
+				.andExpect(status().isUnauthorized())
+				// Ensure it appears we are authenticated with user
+				.andExpect(unauthenticated());
+	}
+
 	@EnableWebSecurity
 	@EnableWebMvc
 	static class Config extends WebSecurityConfigurerAdapter {
@@ -90,7 +93,7 @@ public class DefaultfSecurityRequestsTests {
 					.antMatchers("/admin/**").hasRole("ADMIN")
 					.anyRequest().authenticated()
 					.and()
-				.formLogin();
+				.httpBasic();
 		}
 		// @formatter:on
 

+ 19 - 8
test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserClassLevelAuthenticationTests.java

@@ -15,15 +15,21 @@
  */
 package org.springframework.security.test.web.servlet.showcase.secured;
 
+import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
+import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
+import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Configuration;
 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.WebSecurityConfigurerAdapter;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.test.context.support.WithAnonymousUser;
 import org.springframework.security.test.context.support.WithMockUser;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -33,11 +39,6 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.web.context.WebApplicationContext;
 import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 
-import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
-import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-
 @RunWith(SpringJUnit4ClassRunner.class)
 @ContextConfiguration(classes = WithUserClassLevelAuthenticationTests.Config.class)
 @WebAppConfiguration
@@ -72,6 +73,16 @@ public class WithUserClassLevelAuthenticationTests {
 				.andExpect(authenticated().withUsername("user").withRoles("ADMIN"));
 	}
 
+	@Test
+	@WithAnonymousUser
+	public void requestProtectedUrlWithAnonymous() throws Exception {
+		mvc.perform(get("/"))
+				// Ensure did not get past security
+				.andExpect(status().isUnauthorized())
+				// Ensure not authenticated
+				.andExpect(unauthenticated());
+	}
+
 	@EnableWebSecurity
 	@EnableWebMvc
 	static class Config extends WebSecurityConfigurerAdapter {
@@ -84,7 +95,7 @@ public class WithUserClassLevelAuthenticationTests {
 					.antMatchers("/admin/**").hasRole("ADMIN")
 					.anyRequest().authenticated()
 					.and()
-				.formLogin();
+				.httpBasic();
 		}
 		// @formatter:on