Selaa lähdekoodia

Allow configuration of jee through nested builder

Issue: gh-5557
Eleftheria Stein 6 vuotta sitten
vanhempi
commit
bfc9538da1

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

@@ -751,6 +751,86 @@ public final class HttpSecurity extends
 		return getOrApply(new JeeConfigurer<>());
 	}
 
+	/**
+	 * Configures container based pre authentication. In this case, authentication
+	 * is managed by the Servlet Container.
+	 *
+	 * <h2>Example Configuration</h2>
+	 *
+	 * The following configuration will use the principal found on the
+	 * {@link HttpServletRequest} and if the user is in the role "ROLE_USER" or
+	 * "ROLE_ADMIN" will add that to the resulting {@link Authentication}.
+	 *
+	 * <pre>
+	 * &#064;Configuration
+	 * &#064;EnableWebSecurity
+	 * public class JeeSecurityConfig extends WebSecurityConfigurerAdapter {
+	 *
+	 * 	&#064;Override
+	 * 	protected void configure(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 			.authorizeRequests()
+	 * 				.antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)
+	 * 				.and()
+	 * 			.jee(jee ->
+	 * 				jee
+	 * 					.mappableRoles(&quot;USER&quot;, &quot;ADMIN&quot;)
+	 * 			);
+	 * 	}
+	 * }
+	 * </pre>
+	 *
+	 * Developers wishing to use pre authentication with the container will need to ensure
+	 * their web.xml configures the security constraints. For example, the web.xml (there
+	 * is no equivalent Java based configuration supported by the Servlet specification)
+	 * might look like:
+	 *
+	 * <pre>
+	 * &lt;login-config&gt;
+	 *     &lt;auth-method&gt;FORM&lt;/auth-method&gt;
+	 *     &lt;form-login-config&gt;
+	 *         &lt;form-login-page&gt;/login&lt;/form-login-page&gt;
+	 *         &lt;form-error-page&gt;/login?error&lt;/form-error-page&gt;
+	 *     &lt;/form-login-config&gt;
+	 * &lt;/login-config&gt;
+	 *
+	 * &lt;security-role&gt;
+	 *     &lt;role-name&gt;ROLE_USER&lt;/role-name&gt;
+	 * &lt;/security-role&gt;
+	 * &lt;security-constraint&gt;
+	 *     &lt;web-resource-collection&gt;
+	 *     &lt;web-resource-name&gt;Public&lt;/web-resource-name&gt;
+	 *         &lt;description&gt;Matches unconstrained pages&lt;/description&gt;
+	 *         &lt;url-pattern&gt;/login&lt;/url-pattern&gt;
+	 *         &lt;url-pattern&gt;/logout&lt;/url-pattern&gt;
+	 *         &lt;url-pattern&gt;/resources/*&lt;/url-pattern&gt;
+	 *     &lt;/web-resource-collection&gt;
+	 * &lt;/security-constraint&gt;
+	 * &lt;security-constraint&gt;
+	 *     &lt;web-resource-collection&gt;
+	 *         &lt;web-resource-name&gt;Secured Areas&lt;/web-resource-name&gt;
+	 *         &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
+	 *     &lt;/web-resource-collection&gt;
+	 *     &lt;auth-constraint&gt;
+	 *         &lt;role-name&gt;ROLE_USER&lt;/role-name&gt;
+	 *     &lt;/auth-constraint&gt;
+	 * &lt;/security-constraint&gt;
+	 * </pre>
+	 *
+	 * Last you will need to configure your container to contain the user with the correct
+	 * roles. This configuration is specific to the Servlet Container, so consult your
+	 * Servlet Container's documentation.
+	 *
+	 * @param jeeCustomizer the {@link Customizer} to provide more options for
+	 * the {@link JeeConfigurer}
+	 * @return the {@link HttpSecurity} for further customizations
+	 * @throws Exception
+	 */
+	public HttpSecurity jee(Customizer<JeeConfigurer<HttpSecurity>> jeeCustomizer) throws Exception {
+		jeeCustomizer.customize(getOrApply(new JeeConfigurer<>()));
+		return HttpSecurity.this;
+	}
+
 	/**
 	 * Configures X509 based pre authentication.
 	 *

+ 111 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configurers/JeeConfigurerTests.java

@@ -25,6 +25,9 @@ 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.authority.AuthorityUtils;
+import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
+import org.springframework.security.core.userdetails.User;
 import org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource;
 import org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter;
 import org.springframework.test.web.servlet.MockMvc;
@@ -125,4 +128,112 @@ public class JeeConfigurerTests {
 			// @formatter:on
 		}
 	}
+
+	@Test
+	public void requestWhenJeeMappableRolesInLambdaThenAuthenticatedWithMappableRoles() throws Exception {
+		this.spring.register(JeeMappableRolesConfig.class).autowire();
+		Principal user = mock(Principal.class);
+		when(user.getName()).thenReturn("user");
+
+		this.mvc.perform(get("/")
+				.principal(user)
+				.with(request -> {
+					request.addUserRole("ROLE_ADMIN");
+					request.addUserRole("ROLE_USER");
+					return request;
+				}))
+				.andExpect(authenticated().withRoles("USER"));
+	}
+
+	@EnableWebSecurity
+	public static class JeeMappableRolesConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeRequests()
+					.anyRequest().hasRole("USER")
+					.and()
+				.jee(jee ->
+					jee
+						.mappableRoles("USER")
+				);
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void requestWhenJeeMappableAuthoritiesInLambdaThenAuthenticatedWithMappableAuthorities() throws Exception {
+		this.spring.register(JeeMappableAuthoritiesConfig.class).autowire();
+		Principal user = mock(Principal.class);
+		when(user.getName()).thenReturn("user");
+
+		this.mvc.perform(get("/")
+				.principal(user)
+				.with(request -> {
+					request.addUserRole("ROLE_ADMIN");
+					request.addUserRole("ROLE_USER");
+					return request;
+				}))
+				.andExpect(authenticated().withAuthorities(AuthorityUtils.createAuthorityList("ROLE_USER")));
+	}
+
+	@EnableWebSecurity
+	public static class JeeMappableAuthoritiesConfig extends WebSecurityConfigurerAdapter {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeRequests()
+					.anyRequest().hasRole("USER")
+					.and()
+				.jee(jee ->
+					jee
+						.mappableAuthorities("ROLE_USER")
+				);
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void requestWhenCustomAuthenticatedUserDetailsServiceInLambdaThenCustomAuthenticatedUserDetailsServiceUsed()
+			throws Exception {
+		this.spring.register(JeeCustomAuthenticatedUserDetailsServiceConfig.class).autowire();
+		Principal user = mock(Principal.class);
+		User userDetails = new User("user", "N/A", true, true, true, true,
+				AuthorityUtils.createAuthorityList("ROLE_USER"));
+		when(user.getName()).thenReturn("user");
+		when(JeeCustomAuthenticatedUserDetailsServiceConfig.authenticationUserDetailsService.loadUserDetails(any()))
+				.thenReturn(userDetails);
+
+		this.mvc.perform(get("/")
+				.principal(user)
+				.with(request -> {
+					request.addUserRole("ROLE_ADMIN");
+					request.addUserRole("ROLE_USER");
+					return request;
+				}))
+				.andExpect(authenticated().withRoles("USER"));
+	}
+
+	@EnableWebSecurity
+	public static class JeeCustomAuthenticatedUserDetailsServiceConfig extends WebSecurityConfigurerAdapter {
+		static AuthenticationUserDetailsService authenticationUserDetailsService =
+				mock(AuthenticationUserDetailsService.class);
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeRequests()
+					.anyRequest().hasRole("USER")
+					.and()
+				.jee(jee ->
+					jee
+						.authenticatedUserDetailsService(authenticationUserDetailsService)
+				);
+			// @formatter:on
+		}
+	}
 }