Bläddra i källkod

Allow configuration of anonymous through nested builder

Issue: gh-5557
Eleftheria Stein 6 år sedan
förälder
incheckning
ae8e12f049

+ 70 - 0
config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

@@ -1531,6 +1531,76 @@ public final class HttpSecurity extends
 		return getOrApply(new AnonymousConfigurer<>());
 	}
 
+	/**
+	 * Allows configuring how an anonymous user is represented. This is automatically
+	 * applied when used in conjunction with {@link WebSecurityConfigurerAdapter}. By
+	 * default anonymous users will be represented with an
+	 * {@link org.springframework.security.authentication.AnonymousAuthenticationToken}
+	 * and contain the role "ROLE_ANONYMOUS".
+	 *
+	 * <h2>Example Configuration</h2>
+	 *
+	 * The following configuration demonstrates how to specify that anonymous users should
+	 * contain the role "ROLE_ANON" instead.
+	 *
+	 * <pre>
+	 * &#064;Configuration
+	 * &#064;EnableWebSecurity
+	 * public class AnononymousSecurityConfig extends WebSecurityConfigurerAdapter {
+	 *
+	 * 	&#064;Override
+	 * 	protected void configure(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 			.authorizeRequests(authorizeRequests ->
+	 * 				authorizeRequests
+	 * 					.antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)
+	 * 			)
+	 * 			.formLogin(withDefaults())
+	 * 			// sample anonymous customization
+	 * 			.anonymous(anonymous ->
+	 * 				anonymous
+	 * 					.authorities(&quot;ROLE_ANON&quot;)
+	 * 			)
+	 * 	}
+	 * }
+	 * </pre>
+	 *
+	 * The following demonstrates how to represent anonymous users as null. Note that this
+	 * can cause {@link NullPointerException} in code that assumes anonymous
+	 * authentication is enabled.
+	 *
+	 * <pre>
+	 * &#064;Configuration
+	 * &#064;EnableWebSecurity
+	 * public class AnonymousSecurityConfig extends WebSecurityConfigurerAdapter {
+	 *
+	 * 	&#064;Override
+	 * 	protected void configure(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 			.authorizeRequests(authorizeRequests ->
+	 * 				authorizeRequests
+	 * 					.antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)
+	 * 			)
+	 * 			.formLogin(withDefaults())
+	 * 			// sample anonymous customization
+	 * 			.anonymous(anonymous ->
+	 * 				anonymous.disabled()
+	 * 			);
+	 * 	}
+	 * }
+	 * </pre>
+	 *
+	 * @param anonymousCustomizer the {@link Customizer} to provide more options for
+	 * the {@link AnonymousConfigurer}
+	 * @return the {@link HttpSecurity} for further customizations
+	 * @throws Exception
+	 */
+	public HttpSecurity anonymous(Customizer<AnonymousConfigurer<HttpSecurity>> anonymousCustomizer) throws Exception {
+		anonymousCustomizer.customize(getOrApply(new AnonymousConfigurer<>()));
+		return HttpSecurity.this;
+	}
+
+
 	/**
 	 * Specifies to support form based authentication. If
 	 * {@link FormLoginConfigurer#loginPage(String)} is not specified a default login page

+ 98 - 8
config/src/test/java/org/springframework/security/config/annotation/web/configurers/AnonymousConfigurerTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2018 the original author or authors.
+ * 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.
@@ -18,18 +18,22 @@ package org.springframework.security.config.annotation.web.configurers;
 import org.junit.Rule;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
+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.annotation.AuthenticationPrincipal;
+import org.springframework.security.core.userdetails.PasswordEncodedUser;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 
+import static org.springframework.security.config.Customizer.withDefaults;
 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
 /**
  * @author Rob Winch
@@ -44,7 +48,7 @@ public class AnonymousConfigurerTests {
 
 	@Test
 	public void requestWhenAnonymousTwiceInvokedThenDoesNotOverride() throws Exception {
-		this.spring.register(InvokeTwiceDoesNotOverride.class).autowire();
+		this.spring.register(InvokeTwiceDoesNotOverride.class, PrincipalController.class).autowire();
 
 		this.mockMvc.perform(get("/"))
 			.andExpect(content().string("principal"));
@@ -63,13 +67,99 @@ public class AnonymousConfigurerTests {
 					.and()
 				.anonymous();
 		}
+	}
+
+	@Test
+	public void requestWhenAnonymousPrincipalInLambdaThenPrincipalUsed() throws Exception {
+		this.spring.register(AnonymousPrincipalInLambdaConfig.class, PrincipalController.class).autowire();
+
+		this.mockMvc.perform(get("/"))
+				.andExpect(content().string("principal"));
+	}
+
+	@EnableWebSecurity
+	@EnableWebMvc
+	static class AnonymousPrincipalInLambdaConfig extends WebSecurityConfigurerAdapter {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.anonymous(anonymous ->
+					anonymous
+						.principal("principal")
+				);
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void requestWhenAnonymousDisabledInLambdaThenRespondsWithForbidden() throws Exception {
+		this.spring.register(AnonymousDisabledInLambdaConfig.class, PrincipalController.class).autowire();
+
+		this.mockMvc.perform(get("/"))
+				.andExpect(status().isForbidden());
+	}
+
+	@EnableWebSecurity
+	static class AnonymousDisabledInLambdaConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeRequests(authorizeRequests ->
+					authorizeRequests
+						.anyRequest().permitAll()
+				)
+				.anonymous(AbstractHttpConfigurer::disable);
+			// @formatter:on
+		}
+
+		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+			// @formatter:off
+			auth
+				.inMemoryAuthentication()
+					.withUser(PasswordEncodedUser.user());
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void requestWhenAnonymousWithDefaultsInLambdaThenRespondsWithOk() throws Exception {
+		this.spring.register(AnonymousWithDefaultsInLambdaConfig.class, PrincipalController.class).autowire();
+
+		this.mockMvc.perform(get("/"))
+				.andExpect(status().isOk());
+	}
+
+	@EnableWebSecurity
+	static class AnonymousWithDefaultsInLambdaConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeRequests(authorizeRequests ->
+					authorizeRequests
+						.anyRequest().permitAll()
+				)
+				.anonymous(withDefaults());
+			// @formatter:on
+		}
+
+		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+			// @formatter:off
+			auth
+				.inMemoryAuthentication()
+					.withUser(PasswordEncodedUser.user());
+			// @formatter:on
+		}
+	}
 
-		@RestController
-		static class PrincipalController {
-			@GetMapping("/")
-			String principal(@AuthenticationPrincipal String principal) {
-				return principal;
-			}
+	@RestController
+	static class PrincipalController {
+		@GetMapping("/")
+		String principal(@AuthenticationPrincipal String principal) {
+			return principal;
 		}
 	}
 }