浏览代码

Add AuthorizationManager

Closes gh-8900
Evgeniy Cheban 5 年之前
父节点
当前提交
34b4b1054f
共有 15 个文件被更改,包括 2052 次插入1 次删除
  1. 2 0
      config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterComparator.java
  2. 87 1
      config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java
  3. 289 0
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java
  4. 628 0
      config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java
  5. 65 0
      core/src/main/java/org/springframework/security/authorization/AuthenticatedAuthorizationManager.java
  6. 135 0
      core/src/main/java/org/springframework/security/authorization/AuthorityAuthorizationManager.java
  7. 57 0
      core/src/main/java/org/springframework/security/authorization/AuthorizationManager.java
  8. 77 0
      core/src/test/java/org/springframework/security/authorization/AuthenticatedAuthorizationManagerTests.java
  9. 170 0
      core/src/test/java/org/springframework/security/authorization/AuthorityAuthorizationManagerTests.java
  10. 65 0
      core/src/test/java/org/springframework/security/authorization/AuthorizationManagerTests.java
  11. 69 0
      web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java
  12. 124 0
      web/src/main/java/org/springframework/security/web/access/intercept/DelegatingAuthorizationManager.java
  13. 72 0
      web/src/main/java/org/springframework/security/web/access/intercept/RequestAuthorizationContext.java
  14. 128 0
      web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java
  15. 84 0
      web/src/test/java/org/springframework/security/web/access/intercept/DelegatingAuthorizationManagerTests.java

+ 2 - 0
config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterComparator.java

@@ -25,6 +25,7 @@ import javax.servlet.Filter;
 
 import org.springframework.security.web.access.ExceptionTranslationFilter;
 import org.springframework.security.web.access.channel.ChannelProcessingFilter;
+import org.springframework.security.web.access.intercept.AuthorizationFilter;
 import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
 import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@@ -111,6 +112,7 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
 		put(SessionManagementFilter.class, order.next());
 		put(ExceptionTranslationFilter.class, order.next());
 		put(FilterSecurityInterceptor.class, order.next());
+		put(AuthorizationFilter.class, order.next());
 		put(SwitchUserFilter.class, order.next());
 	}
 

+ 87 - 1
config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2020 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.
@@ -40,6 +40,8 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer;
+import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
+import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry;
 import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
 import org.springframework.security.config.annotation.web.configurers.CorsConfigurer;
 import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
@@ -1254,6 +1256,90 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
 		return HttpSecurity.this;
 	}
 
+	/**
+	 * Allows restricting access based upon the {@link HttpServletRequest} using
+	 * {@link RequestMatcher} implementations (i.e. via URL patterns).
+	 *
+	 * <h2>Example Configurations</h2>
+	 *
+	 * The most basic example is to configure all URLs to require the role "ROLE_USER".
+	 * The configuration below requires authentication to every URL and will grant access
+	 * to both the user "admin" and "user".
+	 *
+	 * <pre>
+	 * &#064;Configuration
+	 * &#064;EnableWebSecurity
+	 * public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
+	 *
+	 * 	&#064;Override
+	 * 	protected void configure(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 			.authorizeHttpRequests((authorizeHttpRequests) ->
+	 * 				authorizeHttpRequests
+	 * 					.antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)
+	 * 			)
+	 * 			.formLogin(withDefaults());
+	 * 	}
+	 * }
+	 * </pre>
+	 *
+	 * We can also configure multiple URLs. The configuration below requires
+	 * authentication to every URL and will grant access to URLs starting with /admin/ to
+	 * only the "admin" user. All other URLs either user can access.
+	 *
+	 * <pre>
+	 * &#064;Configuration
+	 * &#064;EnableWebSecurity
+	 * public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
+	 *
+	 * 	&#064;Override
+	 * 	protected void configure(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 			.authorizeHttpRequests((authorizeHttpRequests) ->
+	 * 				authorizeHttpRequests
+	 * 					.antMatchers(&quot;/admin/**&quot;).hasRole(&quot;ADMIN&quot;)
+	 * 					.antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)
+	 * 			)
+	 * 			.formLogin(withDefaults());
+	 * 	}
+	 * }
+	 * </pre>
+	 *
+	 * Note that the matchers are considered in order. Therefore, the following is invalid
+	 * because the first matcher matches every request and will never get to the second
+	 * mapping:
+	 *
+	 * <pre>
+	 * &#064;Configuration
+	 * &#064;EnableWebSecurity
+	 * public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
+	 *
+	 * 	&#064;Override
+	 * 	protected void configure(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 		 	.authorizeHttpRequests((authorizeHttpRequests) ->
+	 * 		 		authorizeHttpRequests
+	 * 			 		.antMatchers(&quot;/**&quot;).hasRole(&quot;USER&quot;)
+	 * 			 		.antMatchers(&quot;/admin/**&quot;).hasRole(&quot;ADMIN&quot;)
+	 * 		 	);
+	 * 	}
+	 * }
+	 * </pre>
+	 * @param authorizeHttpRequestsCustomizer the {@link Customizer} to provide more
+	 * options for the {@link AuthorizationManagerRequestMatcherRegistry}
+	 * @return the {@link HttpSecurity} for further customizations
+	 * @throws Exception
+	 * @see #requestMatcher(RequestMatcher)
+	 */
+	public HttpSecurity authorizeHttpRequests(
+			Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer)
+			throws Exception {
+		ApplicationContext context = getContext();
+		authorizeHttpRequestsCustomizer
+				.customize(getOrApply(new AuthorizeHttpRequestsConfigurer<>(context)).getRegistry());
+		return HttpSecurity.this;
+	}
+
 	/**
 	 * Allows configuring the Request Cache. For example, a protected page (/protected)
 	 * may be requested prior to authentication. The application will redirect the user to

+ 289 - 0
config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java

@@ -0,0 +1,289 @@
+/*
+ * Copyright 2002-2020 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 java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
+import org.springframework.security.authorization.AuthorityAuthorizationManager;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.config.annotation.ObjectPostProcessor;
+import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
+import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
+import org.springframework.security.web.access.intercept.AuthorizationFilter;
+import org.springframework.security.web.access.intercept.DelegatingAuthorizationManager;
+import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
+import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.util.Assert;
+
+/**
+ * Adds a URL based authorization using {@link AuthorizationManager}.
+ *
+ * @param <H> the type of {@link HttpSecurityBuilder} that is being configured.
+ * @author Evgeniy Cheban
+ */
+public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
+		extends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {
+
+	private final AuthorizationManagerRequestMatcherRegistry registry;
+
+	/**
+	 * Creates an instance.
+	 * @param context the {@link ApplicationContext} to use
+	 */
+	public AuthorizeHttpRequestsConfigurer(ApplicationContext context) {
+		this.registry = new AuthorizationManagerRequestMatcherRegistry(context);
+	}
+
+	/**
+	 * The {@link AuthorizationManagerRequestMatcherRegistry} is what users will interact
+	 * with after applying the {@link AuthorizeHttpRequestsConfigurer}.
+	 * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
+	 * customizations
+	 */
+	public AuthorizationManagerRequestMatcherRegistry getRegistry() {
+		return this.registry;
+	}
+
+	@Override
+	public void configure(H http) {
+		AuthorizationManager<HttpServletRequest> authorizationManager = this.registry.createAuthorizationManager();
+		AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
+		http.addFilter(postProcess(authorizationFilter));
+	}
+
+	private AuthorizationManagerRequestMatcherRegistry addMapping(List<? extends RequestMatcher> matchers,
+			AuthorizationManager<RequestAuthorizationContext> manager) {
+		for (RequestMatcher matcher : matchers) {
+			this.registry.addMapping(matcher, manager);
+		}
+		return this.registry;
+	}
+
+	/**
+	 * Registry for mapping a {@link RequestMatcher} to an {@link AuthorizationManager}.
+	 *
+	 * @author Evgeniy Cheban
+	 */
+	public final class AuthorizationManagerRequestMatcherRegistry
+			extends AbstractRequestMatcherRegistry<AuthorizedUrl> {
+
+		private final DelegatingAuthorizationManager.Builder managerBuilder = DelegatingAuthorizationManager.builder();
+
+		private List<RequestMatcher> unmappedMatchers;
+
+		private int mappingCount;
+
+		private AuthorizationManagerRequestMatcherRegistry(ApplicationContext context) {
+			setApplicationContext(context);
+		}
+
+		private void addMapping(RequestMatcher matcher, AuthorizationManager<RequestAuthorizationContext> manager) {
+			this.unmappedMatchers = null;
+			this.managerBuilder.add(matcher, manager);
+			this.mappingCount++;
+		}
+
+		private AuthorizationManager<HttpServletRequest> createAuthorizationManager() {
+			Assert.state(this.unmappedMatchers == null,
+					() -> "An incomplete mapping was found for " + this.unmappedMatchers
+							+ ". Try completing it with something like requestUrls().<something>.hasRole('USER')");
+			Assert.state(this.mappingCount > 0,
+					"At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())");
+			return postProcess(this.managerBuilder.build());
+		}
+
+		@Override
+		public MvcMatchersAuthorizedUrl mvcMatchers(String... mvcPatterns) {
+			return mvcMatchers(null, mvcPatterns);
+		}
+
+		@Override
+		public MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method, String... mvcPatterns) {
+			return new MvcMatchersAuthorizedUrl(createMvcMatchers(method, mvcPatterns));
+		}
+
+		@Override
+		protected AuthorizedUrl chainRequestMatchers(List<RequestMatcher> requestMatchers) {
+			this.unmappedMatchers = requestMatchers;
+			return new AuthorizedUrl(requestMatchers);
+		}
+
+		/**
+		 * Adds an {@link ObjectPostProcessor} for this class.
+		 * @param objectPostProcessor the {@link ObjectPostProcessor} to use
+		 * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
+		 * customizations
+		 */
+		public AuthorizationManagerRequestMatcherRegistry withObjectPostProcessor(
+				ObjectPostProcessor<?> objectPostProcessor) {
+			addObjectPostProcessor(objectPostProcessor);
+			return this;
+		}
+
+		/**
+		 * Return the {@link HttpSecurityBuilder} when done using the
+		 * {@link AuthorizeHttpRequestsConfigurer}. This is useful for method chaining.
+		 * @return the {@link HttpSecurityBuilder} for further customizations
+		 */
+		public H and() {
+			return AuthorizeHttpRequestsConfigurer.this.and();
+		}
+
+	}
+
+	/**
+	 * An {@link AuthorizeHttpRequestsConfigurer.AuthorizedUrl} that allows optionally
+	 * configuring the {@link MvcRequestMatcher#setServletPath(String)}.
+	 *
+	 * @author Evgeniy Cheban
+	 */
+	public final class MvcMatchersAuthorizedUrl extends AuthorizedUrl {
+
+		private MvcMatchersAuthorizedUrl(List<MvcRequestMatcher> matchers) {
+			super(matchers);
+		}
+
+		/**
+		 * Configures <code>servletPath</code> to {@link MvcRequestMatcher}s.
+		 * @param servletPath the servlet path
+		 * @return the {@link MvcMatchersAuthorizedUrl} for further customizations
+		 */
+		@SuppressWarnings("unchecked")
+		public MvcMatchersAuthorizedUrl servletPath(String servletPath) {
+			for (MvcRequestMatcher matcher : (List<MvcRequestMatcher>) getMatchers()) {
+				matcher.setServletPath(servletPath);
+			}
+			return this;
+		}
+
+	}
+
+	/**
+	 * An object that allows configuring the {@link AuthorizationManager} for
+	 * {@link RequestMatcher}s.
+	 *
+	 * @author Evgeniy Cheban
+	 */
+	public class AuthorizedUrl {
+
+		private final List<? extends RequestMatcher> matchers;
+
+		/**
+		 * Creates an instance.
+		 * @param matchers the {@link RequestMatcher} instances to map
+		 */
+		AuthorizedUrl(List<? extends RequestMatcher> matchers) {
+			this.matchers = matchers;
+		}
+
+		protected List<? extends RequestMatcher> getMatchers() {
+			return this.matchers;
+		}
+
+		/**
+		 * Specify that URLs are allowed by anyone.
+		 * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
+		 * customizations
+		 */
+		public AuthorizationManagerRequestMatcherRegistry permitAll() {
+			return access((a, o) -> new AuthorizationDecision(true));
+		}
+
+		/**
+		 * Specify that URLs are not allowed by anyone.
+		 * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
+		 * customizations
+		 */
+		public AuthorizationManagerRequestMatcherRegistry denyAll() {
+			return access((a, o) -> new AuthorizationDecision(false));
+		}
+
+		/**
+		 * Specifies a user requires a role.
+		 * @param role the role that should be required which is prepended with ROLE_
+		 * automatically (i.e. USER, ADMIN, etc). It should not start with ROLE_
+		 * @return {@link AuthorizationManagerRequestMatcherRegistry} for further
+		 * customizations
+		 */
+		public AuthorizationManagerRequestMatcherRegistry hasRole(String role) {
+			return access(AuthorityAuthorizationManager.hasRole(role));
+		}
+
+		/**
+		 * Specifies that a user requires one of many roles.
+		 * @param roles the roles that the user should have at least one of (i.e. ADMIN,
+		 * USER, etc). Each role should not start with ROLE_ since it is automatically
+		 * prepended already
+		 * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
+		 * customizations
+		 */
+		public AuthorizationManagerRequestMatcherRegistry hasAnyRole(String... roles) {
+			return access(AuthorityAuthorizationManager.hasAnyRole(roles));
+		}
+
+		/**
+		 * Specifies a user requires an authority.
+		 * @param authority the authority that should be required
+		 * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
+		 * customizations
+		 */
+		public AuthorizationManagerRequestMatcherRegistry hasAuthority(String authority) {
+			return access(AuthorityAuthorizationManager.hasAuthority(authority));
+		}
+
+		/**
+		 * Specifies that a user requires one of many authorities.
+		 * @param authorities the authorities that the user should have at least one of
+		 * (i.e. ROLE_USER, ROLE_ADMIN, etc)
+		 * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
+		 * customizations
+		 */
+		public AuthorizationManagerRequestMatcherRegistry hasAnyAuthority(String... authorities) {
+			return access(AuthorityAuthorizationManager.hasAnyAuthority(authorities));
+		}
+
+		/**
+		 * Specify that URLs are allowed by any authenticated user.
+		 * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
+		 * customizations
+		 */
+		public AuthorizationManagerRequestMatcherRegistry authenticated() {
+			return access(AuthenticatedAuthorizationManager.authenticated());
+		}
+
+		/**
+		 * Allows specifying a custom {@link AuthorizationManager}.
+		 * @param manager the {@link AuthorizationManager} to use
+		 * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
+		 * customizations
+		 */
+		public AuthorizationManagerRequestMatcherRegistry access(
+				AuthorizationManager<RequestAuthorizationContext> manager) {
+			Assert.notNull(manager, "manager cannot be null");
+			return AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);
+		}
+
+	}
+
+}

+ 628 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java

@@ -0,0 +1,628 @@
+/*
+ * Copyright 2002-2020 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.junit.Rule;
+import org.junit.Test;
+
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.config.annotation.ObjectPostProcessor;
+import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.test.SpringTestRule;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.access.intercept.AuthorizationFilter;
+import org.springframework.security.web.access.intercept.DelegatingAuthorizationManager;
+import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.springframework.security.config.Customizer.withDefaults;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
+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.status;
+
+/**
+ * Tests for {@link AuthorizeHttpRequestsConfigurer}.
+ *
+ * @author Evgeniy Cheban
+ */
+public class AuthorizeHttpRequestsConfigurerTests {
+
+	@Rule
+	public final SpringTestRule spring = new SpringTestRule();
+
+	@Autowired
+	MockMvc mvc;
+
+	@Test
+	public void configureWhenAuthorizedHttpRequestsAndNoRequestsThenException() {
+		assertThatExceptionOfType(BeanCreationException.class)
+				.isThrownBy(() -> this.spring.register(NoRequestsConfig.class).autowire()).withMessageContaining(
+						"At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())");
+	}
+
+	@Test
+	public void configureWhenAnyRequestIncompleteMappingThenException() {
+		assertThatExceptionOfType(BeanCreationException.class)
+				.isThrownBy(() -> this.spring.register(IncompleteMappingConfig.class).autowire())
+				.withMessageContaining("An incomplete mapping was found for ");
+	}
+
+	@Test
+	public void configureWhenMvcMatcherAfterAnyRequestThenException() {
+		assertThatExceptionOfType(BeanCreationException.class)
+				.isThrownBy(() -> this.spring.register(AfterAnyRequestConfig.class).autowire())
+				.withMessageContaining("Can't configure mvcMatchers after anyRequest");
+	}
+
+	@Test
+	public void configureMvcMatcherAccessAuthorizationManagerWhenNotNullThenVerifyUse() throws Exception {
+		CustomAuthorizationManagerConfig.authorizationManager = mock(AuthorizationManager.class);
+		this.spring.register(CustomAuthorizationManagerConfig.class, BasicController.class).autowire();
+		this.mvc.perform(get("/")).andExpect(status().isOk());
+		verify(CustomAuthorizationManagerConfig.authorizationManager).check(any(), any());
+	}
+
+	@Test
+	public void configureMvcMatcherAccessAuthorizationManagerWhenNullThenException() {
+		CustomAuthorizationManagerConfig.authorizationManager = null;
+		assertThatExceptionOfType(BeanCreationException.class)
+				.isThrownBy(() -> this.spring.register(CustomAuthorizationManagerConfig.class).autowire())
+				.withMessageContaining("manager cannot be null");
+	}
+
+	@Test
+	public void configureWhenObjectPostProcessorRegisteredThenInvokedOnAuthorizationManagerAndAuthorizationFilter() {
+		this.spring.register(ObjectPostProcessorConfig.class).autowire();
+		verify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(DelegatingAuthorizationManager.class));
+		verify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(AuthorizationFilter.class));
+	}
+
+	@Test
+	public void getWhenHasAnyAuthorityRoleUserConfiguredAndAuthorityIsRoleUserThenRespondsWithOk() throws Exception {
+		this.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithUser = get("/")
+				.with(user("user")
+				.authorities(new SimpleGrantedAuthority("ROLE_USER")));
+		// @formatter:on
+		this.mvc.perform(requestWithUser).andExpect(status().isOk());
+	}
+
+	@Test
+	public void getWhenHasAnyAuthorityRoleUserConfiguredAndAuthorityIsRoleAdminThenRespondsWithForbidden()
+			throws Exception {
+		this.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithAdmin = get("/")
+				.with(user("user")
+				.authorities(new SimpleGrantedAuthority("ROLE_ADMIN")));
+		// @formatter:on
+		this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());
+	}
+
+	@Test
+	public void getWhenHasAnyAuthorityRoleUserConfiguredAndNoAuthorityThenRespondsWithUnauthorized() throws Exception {
+		this.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire();
+		this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
+	}
+
+	@Test
+	public void getWhenHasAuthorityRoleUserConfiguredAndAuthorityIsRoleUserThenRespondsWithOk() throws Exception {
+		this.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithUser = get("/")
+				.with(user("user")
+				.authorities(new SimpleGrantedAuthority("ROLE_USER")));
+		// @formatter:on
+		this.mvc.perform(requestWithUser).andExpect(status().isOk());
+	}
+
+	@Test
+	public void getWhenHasAuthorityRoleUserConfiguredAndAuthorityIsRoleAdminThenRespondsWithForbidden()
+			throws Exception {
+		this.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithAdmin = get("/")
+				.with(user("user")
+				.authorities(new SimpleGrantedAuthority("ROLE_ADMIN")));
+		// @formatter:on
+		this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());
+	}
+
+	@Test
+	public void getWhenHasAuthorityRoleUserConfiguredAndNoAuthorityThenRespondsWithUnauthorized() throws Exception {
+		this.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire();
+		this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
+	}
+
+	@Test
+	public void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleUserThenRespondsWithOk() throws Exception {
+		this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithUser = get("/")
+				.with(user("user")
+				.authorities(new SimpleGrantedAuthority("ROLE_USER")));
+		// @formatter:on
+		this.mvc.perform(requestWithUser).andExpect(status().isOk());
+	}
+
+	@Test
+	public void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleAdminThenRespondsWithOk() throws Exception {
+		this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithAdmin = get("/")
+				.with(user("user")
+				.authorities(new SimpleGrantedAuthority("ROLE_ADMIN")));
+		// @formatter:on
+		this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
+	}
+
+	@Test
+	public void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleOtherThenRespondsWithForbidden()
+			throws Exception {
+		this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithOther = get("/")
+				.with(user("user")
+				.authorities(new SimpleGrantedAuthority("ROLE_OTHER")));
+		// @formatter:on
+		this.mvc.perform(requestWithOther).andExpect(status().isForbidden());
+	}
+
+	@Test
+	public void getWhenAuthorityRoleUserOrAdminAuthRequiredAndNoUserThenRespondsWithUnauthorized() throws Exception {
+		this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();
+		this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
+	}
+
+	@Test
+	public void getWhenHasRoleUserConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
+		this.spring.register(RoleUserConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithUser = get("/")
+				.with(user("user")
+				.roles("USER"));
+		// @formatter:on
+		this.mvc.perform(requestWithUser).andExpect(status().isOk());
+	}
+
+	@Test
+	public void getWhenHasRoleUserConfiguredAndRoleIsAdminThenRespondsWithForbidden() throws Exception {
+		this.spring.register(RoleUserConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithAdmin = get("/")
+				.with(user("user")
+				.roles("ADMIN"));
+		// @formatter:on
+		this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());
+	}
+
+	@Test
+	public void getWhenRoleUserOrAdminConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
+		this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithUser = get("/")
+				.with(user("user")
+				.roles("USER"));
+		// @formatter:on
+		this.mvc.perform(requestWithUser).andExpect(status().isOk());
+	}
+
+	@Test
+	public void getWhenRoleUserOrAdminConfiguredAndRoleIsAdminThenRespondsWithOk() throws Exception {
+		this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithAdmin = get("/")
+				.with(user("user")
+				.roles("ADMIN"));
+		// @formatter:on
+		this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
+	}
+
+	@Test
+	public void getWhenRoleUserOrAdminConfiguredAndRoleIsOtherThenRespondsWithForbidden() throws Exception {
+		this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithRoleOther = get("/")
+				.with(user("user")
+				.roles("OTHER"));
+		// @formatter:on
+		this.mvc.perform(requestWithRoleOther).andExpect(status().isForbidden());
+	}
+
+	@Test
+	public void getWhenDenyAllConfiguredAndNoUserThenRespondsWithUnauthorized() throws Exception {
+		this.spring.register(DenyAllConfig.class, BasicController.class).autowire();
+		this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
+	}
+
+	@Test
+	public void getWhenDenyAllConfiguredAndUserLoggedInThenRespondsWithForbidden() throws Exception {
+		this.spring.register(DenyAllConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithUser = get("/")
+				.with(user("user")
+				.roles("USER"));
+		// @formatter:on
+		this.mvc.perform(requestWithUser).andExpect(status().isForbidden());
+	}
+
+	@Test
+	public void getWhenPermitAllConfiguredAndNoUserThenRespondsWithOk() throws Exception {
+		this.spring.register(PermitAllConfig.class, BasicController.class).autowire();
+		this.mvc.perform(get("/")).andExpect(status().isOk());
+	}
+
+	@Test
+	public void getWhenPermitAllConfiguredAndUserLoggedInThenRespondsWithOk() throws Exception {
+		this.spring.register(PermitAllConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithUser = get("/")
+				.with(user("user")
+				.roles("USER"));
+		// @formatter:on
+		this.mvc.perform(requestWithUser).andExpect(status().isOk());
+	}
+
+	@Test
+	public void authorizeHttpRequestsWhenInvokedTwiceThenUsesOriginalConfiguration() throws Exception {
+		this.spring.register(InvokeTwiceDoesNotResetConfig.class, BasicController.class).autowire();
+		this.mvc.perform(post("/").with(csrf())).andExpect(status().isUnauthorized());
+	}
+
+	@Test
+	public void getWhenServletPathRoleAdminConfiguredAndRoleIsUserThenRespondsWithForbidden() throws Exception {
+		this.spring.register(ServletPathConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithUser = get("/spring/")
+				.servletPath("/spring")
+				.with(user("user")
+				.roles("USER"));
+		// @formatter:on
+		this.mvc.perform(requestWithUser).andExpect(status().isForbidden());
+	}
+
+	@Test
+	public void getWhenServletPathRoleAdminConfiguredAndRoleIsAdminThenRespondsWithOk() throws Exception {
+		this.spring.register(ServletPathConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithAdmin = get("/spring/")
+				.servletPath("/spring")
+				.with(user("user")
+				.roles("ADMIN"));
+		// @formatter:on
+		this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
+	}
+
+	@Test
+	public void getWhenAnyRequestAuthenticatedConfiguredAndNoUserThenRespondsWithUnauthorized() throws Exception {
+		this.spring.register(AuthenticatedConfig.class, BasicController.class).autowire();
+		this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
+	}
+
+	@Test
+	public void getWhenAnyRequestAuthenticatedConfiguredAndUserLoggedInThenRespondsWithOk() throws Exception {
+		this.spring.register(AuthenticatedConfig.class, BasicController.class).autowire();
+		// @formatter:off
+		MockHttpServletRequestBuilder requestWithUser = get("/")
+				.with(user("user")
+				.roles("USER"));
+		// @formatter:on
+		this.mvc.perform(requestWithUser).andExpect(status().isOk());
+	}
+
+	@EnableWebSecurity
+	static class NoRequestsConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.authorizeHttpRequests(withDefaults())
+					.build();
+			// @formatter:on
+		}
+
+	}
+
+	@EnableWebSecurity
+	static class IncompleteMappingConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.authorizeHttpRequests(AbstractRequestMatcherRegistry::anyRequest)
+					.build();
+			// @formatter:on
+		}
+
+	}
+
+	@EnableWebSecurity
+	static class AfterAnyRequestConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.authorizeHttpRequests((requests) -> requests
+						.anyRequest().authenticated()
+						.mvcMatchers("/path").hasRole("USER")
+					)
+					.build();
+			// @formatter:on
+		}
+
+	}
+
+	@EnableWebSecurity
+	static class CustomAuthorizationManagerConfig {
+
+		static AuthorizationManager<RequestAuthorizationContext> authorizationManager;
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.authorizeHttpRequests((requests) -> requests
+						.anyRequest().access(authorizationManager)
+					)
+					.build();
+			// @formatter:on
+		}
+
+	}
+
+	@EnableWebSecurity
+	static class ObjectPostProcessorConfig {
+
+		static ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.authorizeHttpRequests((requests) -> requests
+						.anyRequest().authenticated()
+					)
+					.build();
+			// @formatter:on
+		}
+
+		@Bean
+		static ObjectPostProcessor<Object> objectPostProcessor() {
+			return objectPostProcessor;
+		}
+
+	}
+
+	static class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {
+
+		@Override
+		public <O> O postProcess(O object) {
+			return object;
+		}
+
+	}
+
+	@EnableWebSecurity
+	static class RoleUserAnyAuthorityConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.httpBasic()
+						.and()
+					.authorizeHttpRequests((requests) -> requests
+						.anyRequest().hasAnyAuthority("ROLE_USER")
+					)
+					.build();
+			// @formatter:on
+		}
+
+	}
+
+	@EnableWebSecurity
+	static class RoleUserAuthorityConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.httpBasic()
+						.and()
+					.authorizeHttpRequests((requests) -> requests
+						.anyRequest().hasAuthority("ROLE_USER")
+					)
+					.build();
+			// @formatter:on
+		}
+
+	}
+
+	@EnableWebSecurity
+	static class RoleUserOrRoleAdminAuthorityConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.httpBasic()
+						.and()
+					.authorizeHttpRequests((requests) -> requests
+						.anyRequest().hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
+					)
+					.build();
+			// @formatter:on
+		}
+
+	}
+
+	@EnableWebSecurity
+	static class RoleUserConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.authorizeHttpRequests((requests) -> requests
+						.anyRequest().hasRole("USER")
+					)
+					.build();
+			// @formatter:on
+		}
+
+	}
+
+	@EnableWebSecurity
+	static class RoleUserOrAdminConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.authorizeHttpRequests((requests) -> requests
+						.anyRequest().hasAnyRole("USER", "ADMIN")
+					)
+					.build();
+			// @formatter:on
+		}
+
+	}
+
+	@EnableWebSecurity
+	static class DenyAllConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.httpBasic()
+						.and()
+					.authorizeHttpRequests((requests) -> requests
+						.anyRequest().denyAll()
+					)
+					.build();
+			// @formatter:on
+		}
+
+	}
+
+	@EnableWebSecurity
+	static class PermitAllConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.authorizeHttpRequests((requests) -> requests
+						.anyRequest().permitAll()
+					)
+					.build();
+			// @formatter:on
+		}
+
+	}
+
+	@EnableWebSecurity
+	static class InvokeTwiceDoesNotResetConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.httpBasic()
+						.and()
+					.authorizeHttpRequests((requests) -> requests
+						.anyRequest().authenticated()
+					)
+					.authorizeHttpRequests(withDefaults())
+					.build();
+			// @formatter:on
+		}
+
+	}
+
+	@EnableWebMvc
+	@EnableWebSecurity
+	static class ServletPathConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.authorizeHttpRequests((requests) -> requests
+						.mvcMatchers("/").servletPath("/spring").hasRole("ADMIN")
+					)
+					.build();
+			// @formatter:on
+		}
+
+	}
+
+	@EnableWebSecurity
+	static class AuthenticatedConfig {
+
+		@Bean
+		SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+			// @formatter:off
+			return http
+					.httpBasic()
+						.and()
+					.authorizeHttpRequests((requests) -> requests
+						.anyRequest().authenticated()
+					)
+					.build();
+			// @formatter:on
+		}
+
+	}
+
+	@RestController
+	static class BasicController {
+
+		@GetMapping("/")
+		void rootGet() {
+		}
+
+		@PostMapping("/")
+		void rootPost() {
+		}
+
+	}
+
+}

+ 65 - 0
core/src/main/java/org/springframework/security/authorization/AuthenticatedAuthorizationManager.java

@@ -0,0 +1,65 @@
+/*
+ * Copyright 2002-2020 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.authorization;
+
+import java.util.function.Supplier;
+
+import org.springframework.security.authentication.AuthenticationTrustResolver;
+import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
+import org.springframework.security.core.Authentication;
+
+/**
+ * An {@link AuthorizationManager} that determines if the current user is authenticated.
+ *
+ * @param <T> the type of object authorization is being performed against. This does not.
+ * @author Evgeniy Cheban
+ */
+public final class AuthenticatedAuthorizationManager<T> implements AuthorizationManager<T> {
+
+	private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
+
+	/**
+	 * Creates an instance of {@link AuthenticatedAuthorizationManager}.
+	 * @param <T> the type of object being authorized
+	 * @return the new instance
+	 */
+	public static <T> AuthenticatedAuthorizationManager<T> authenticated() {
+		return new AuthenticatedAuthorizationManager<>();
+	}
+
+	/**
+	 * Determines if the current user is authorized by evaluating if the
+	 * {@link Authentication} is not anonymous and authenticated.
+	 * @param authentication the {@link Supplier} of the {@link Authentication} to check
+	 * @param object the {@link T} object to check
+	 * @return an {@link AuthorizationDecision}
+	 */
+	@Override
+	public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
+		boolean granted = isGranted(authentication.get());
+		return new AuthorizationDecision(granted);
+	}
+
+	private boolean isGranted(Authentication authentication) {
+		return authentication != null && isNotAnonymous(authentication) && authentication.isAuthenticated();
+	}
+
+	private boolean isNotAnonymous(Authentication authentication) {
+		return !this.trustResolver.isAnonymous(authentication);
+	}
+
+}

+ 135 - 0
core/src/main/java/org/springframework/security/authorization/AuthorityAuthorizationManager.java

@@ -0,0 +1,135 @@
+/*
+ * Copyright 2002-2020 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.authorization;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Supplier;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.util.Assert;
+
+/**
+ * An {@link AuthorizationManager} that determines if the current user is authorized by
+ * evaluating if the {@link Authentication} contains a specified authority.
+ *
+ * @param <T> the type of object being authorized.
+ * @author Evgeniy Cheban
+ */
+public final class AuthorityAuthorizationManager<T> implements AuthorizationManager<T> {
+
+	private static final String ROLE_PREFIX = "ROLE_";
+
+	private final Set<String> authorities;
+
+	private AuthorityAuthorizationManager(String... authorities) {
+		this.authorities = new HashSet<>(Arrays.asList(authorities));
+	}
+
+	/**
+	 * Creates an instance of {@link AuthorityAuthorizationManager} with the provided
+	 * authority.
+	 * @param role the authority to check for prefixed with "ROLE_"
+	 * @param <T> the type of object being authorized
+	 * @return the new instance
+	 */
+	public static <T> AuthorityAuthorizationManager<T> hasRole(String role) {
+		Assert.notNull(role, "role cannot be null");
+		return hasAuthority(ROLE_PREFIX + role);
+	}
+
+	/**
+	 * Creates an instance of {@link AuthorityAuthorizationManager} with the provided
+	 * authority.
+	 * @param authority the authority to check for
+	 * @param <T> the type of object being authorized
+	 * @return the new instance
+	 */
+	public static <T> AuthorityAuthorizationManager<T> hasAuthority(String authority) {
+		Assert.notNull(authority, "authority cannot be null");
+		return new AuthorityAuthorizationManager<>(authority);
+	}
+
+	/**
+	 * Creates an instance of {@link AuthorityAuthorizationManager} with the provided
+	 * authorities.
+	 * @param roles the authorities to check for prefixed with "ROLE_"
+	 * @param <T> the type of object being authorized
+	 * @return the new instance
+	 */
+	public static <T> AuthorityAuthorizationManager<T> hasAnyRole(String... roles) {
+		Assert.notEmpty(roles, "roles cannot be empty");
+		Assert.noNullElements(roles, "roles cannot contain null values");
+		return hasAnyAuthority(toNamedRolesArray(roles));
+	}
+
+	/**
+	 * Creates an instance of {@link AuthorityAuthorizationManager} with the provided
+	 * authorities.
+	 * @param authorities the authorities to check for
+	 * @param <T> the type of object being authorized
+	 * @return the new instance
+	 */
+	public static <T> AuthorityAuthorizationManager<T> hasAnyAuthority(String... authorities) {
+		Assert.notEmpty(authorities, "authorities cannot be empty");
+		Assert.noNullElements(authorities, "authorities cannot contain null values");
+		return new AuthorityAuthorizationManager<>(authorities);
+	}
+
+	private static String[] toNamedRolesArray(String... roles) {
+		String[] result = new String[roles.length];
+		for (int i = 0; i < roles.length; i++) {
+			result[i] = ROLE_PREFIX + roles[i];
+		}
+		return result;
+	}
+
+	/**
+	 * Determines if the current user is authorized by evaluating if the
+	 * {@link Authentication} contains a specified authority.
+	 * @param authentication the {@link Supplier} of the {@link Authentication} to check
+	 * @param object the {@link T} object to check
+	 * @return an {@link AuthorizationDecision}
+	 */
+	@Override
+	public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
+		boolean granted = isGranted(authentication.get());
+		return new AuthorizationDecision(granted);
+	}
+
+	private boolean isGranted(Authentication authentication) {
+		return authentication != null && authentication.isAuthenticated() && isAuthorized(authentication);
+	}
+
+	private boolean isAuthorized(Authentication authentication) {
+		for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
+			String authority = grantedAuthority.getAuthority();
+			if (this.authorities.contains(authority)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public String toString() {
+		return "AuthorityAuthorizationManager[authorities=" + this.authorities + "]";
+	}
+
+}

+ 57 - 0
core/src/main/java/org/springframework/security/authorization/AuthorizationManager.java

@@ -0,0 +1,57 @@
+/*
+ * Copyright 2002-2020 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.authorization;
+
+import java.util.function.Supplier;
+
+import org.springframework.lang.Nullable;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.core.Authentication;
+
+/**
+ * An Authorization manager which can determine if an {@link Authentication} has access to
+ * a specific object.
+ *
+ * @param <T> the type of object that the authorization check is being done one.
+ * @author Evgeniy Cheban
+ */
+@FunctionalInterface
+public interface AuthorizationManager<T> {
+
+	/**
+	 * Determines if access should be granted for a specific authentication and object.
+	 * @param authentication the {@link Supplier} of the {@link Authentication} to check
+	 * @param object the {@link T} object to check
+	 * @throws AccessDeniedException if access is not granted
+	 */
+	default void verify(Supplier<Authentication> authentication, T object) {
+		AuthorizationDecision decision = check(authentication, object);
+		if (decision != null && !decision.isGranted()) {
+			throw new AccessDeniedException("Access Denied");
+		}
+	}
+
+	/**
+	 * Determines if access is granted for a specific authentication and object.
+	 * @param authentication the {@link Supplier} of the {@link Authentication} to check
+	 * @param object the {@link T} object to check
+	 * @return an {@link AuthorizationDecision} or null if no decision could be made
+	 */
+	@Nullable
+	AuthorizationDecision check(Supplier<Authentication> authentication, T object);
+
+}

+ 77 - 0
core/src/test/java/org/springframework/security/authorization/AuthenticatedAuthorizationManagerTests.java

@@ -0,0 +1,77 @@
+/*
+ * Copyright 2002-2020 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.authorization;
+
+import java.util.function.Supplier;
+
+import org.junit.Test;
+
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.AuthorityUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link AuthenticatedAuthorizationManager}.
+ *
+ * @author Evgeniy Cheban
+ */
+public class AuthenticatedAuthorizationManagerTests {
+
+	@Test
+	public void authenticatedWhenUserNotAnonymousAndAuthenticatedThenGrantedDecision() {
+		AuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();
+		Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN",
+				"ROLE_USER");
+		Object object = new Object();
+
+		assertThat(manager.check(authentication, object).isGranted()).isTrue();
+	}
+
+	@Test
+	public void authenticatedWhenUserNullThenDeniedDecision() {
+		AuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();
+		Supplier<Authentication> authentication = () -> null;
+		Object object = new Object();
+
+		assertThat(manager.check(authentication, object).isGranted()).isFalse();
+	}
+
+	@Test
+	public void authenticatedWhenUserAnonymousThenDeniedDecision() {
+		AuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();
+		Supplier<Authentication> authentication = () -> new AnonymousAuthenticationToken("key", "principal",
+				AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
+		Object object = new Object();
+
+		assertThat(manager.check(authentication, object).isGranted()).isFalse();
+	}
+
+	@Test
+	public void authenticatedWhenUserNotAuthenticatedThenDeniedDecision() {
+		AuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();
+		TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "password", "ROLE_ADMIN",
+				"ROLE_USER");
+		authentication.setAuthenticated(false);
+		Object object = new Object();
+
+		assertThat(manager.check(() -> authentication, object).isGranted()).isFalse();
+	}
+
+}

+ 170 - 0
core/src/test/java/org/springframework/security/authorization/AuthorityAuthorizationManagerTests.java

@@ -0,0 +1,170 @@
+/*
+ * Copyright 2002-2020 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.authorization;
+
+import java.util.function.Supplier;
+
+import org.junit.Test;
+
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.core.Authentication;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+
+/**
+ * Tests for {@link AuthorityAuthorizationManager}.
+ *
+ * @author Evgeniy Cheban
+ */
+public class AuthorityAuthorizationManagerTests {
+
+	@Test
+	public void hasRoleWhenNullThenException() {
+		assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasRole(null))
+				.withMessage("role cannot be null");
+	}
+
+	@Test
+	public void hasAuthorityWhenNullThenException() {
+		assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAuthority(null))
+				.withMessage("authority cannot be null");
+	}
+
+	@Test
+	public void hasAnyRoleWhenNullThenException() {
+		assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole(null))
+				.withMessage("roles cannot be empty");
+	}
+
+	@Test
+	public void hasAnyRoleWhenEmptyThenException() {
+		assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole(new String[] {}))
+				.withMessage("roles cannot be empty");
+	}
+
+	@Test
+	public void hasAnyRoleWhenContainNullThenException() {
+		assertThatIllegalArgumentException()
+				.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole("ADMIN", null, "USER"))
+				.withMessage("roles cannot contain null values");
+	}
+
+	@Test
+	public void hasAnyAuthorityWhenNullThenException() {
+		assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority(null))
+				.withMessage("authorities cannot be empty");
+	}
+
+	@Test
+	public void hasAnyAuthorityWhenEmptyThenException() {
+		assertThatIllegalArgumentException()
+				.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority(new String[] {}))
+				.withMessage("authorities cannot be empty");
+	}
+
+	@Test
+	public void hasAnyAuthorityWhenContainNullThenException() {
+		assertThatIllegalArgumentException()
+				.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority("ADMIN", null, "USER"))
+				.withMessage("authorities cannot contain null values");
+	}
+
+	@Test
+	public void hasRoleWhenUserHasRoleThenGrantedDecision() {
+		AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasRole("ADMIN");
+
+		Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN",
+				"ROLE_USER");
+		Object object = new Object();
+
+		assertThat(manager.check(authentication, object).isGranted()).isTrue();
+	}
+
+	@Test
+	public void hasRoleWhenUserHasNotRoleThenDeniedDecision() {
+		AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasRole("ADMIN");
+
+		Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
+		Object object = new Object();
+
+		assertThat(manager.check(authentication, object).isGranted()).isFalse();
+	}
+
+	@Test
+	public void hasAuthorityWhenUserHasAuthorityThenGrantedDecision() {
+		AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAuthority("ADMIN");
+
+		Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ADMIN",
+				"USER");
+		Object object = new Object();
+
+		assertThat(manager.check(authentication, object).isGranted()).isTrue();
+	}
+
+	@Test
+	public void hasAuthorityWhenUserHasNotAuthorityThenDeniedDecision() {
+		AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAuthority("ADMIN");
+
+		Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "USER");
+		Object object = new Object();
+
+		assertThat(manager.check(authentication, object).isGranted()).isFalse();
+	}
+
+	@Test
+	public void hasAnyRoleWhenUserHasAnyRoleThenGrantedDecision() {
+		AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyRole("ADMIN", "USER");
+
+		Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
+		Object object = new Object();
+
+		assertThat(manager.check(authentication, object).isGranted()).isTrue();
+	}
+
+	@Test
+	public void hasAnyRoleWhenUserHasNotAnyRoleThenDeniedDecision() {
+		AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyRole("ADMIN", "USER");
+
+		Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
+				"ROLE_ANONYMOUS");
+		Object object = new Object();
+
+		assertThat(manager.check(authentication, object).isGranted()).isFalse();
+	}
+
+	@Test
+	public void hasAnyAuthorityWhenUserHasAnyAuthorityThenGrantedDecision() {
+		AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyAuthority("ADMIN", "USER");
+
+		Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "USER");
+		Object object = new Object();
+
+		assertThat(manager.check(authentication, object).isGranted()).isTrue();
+	}
+
+	@Test
+	public void hasAnyAuthorityWhenUserHasNotAnyAuthorityThenDeniedDecision() {
+		AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyAuthority("ADMIN", "USER");
+
+		Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ANONYMOUS");
+		Object object = new Object();
+
+		assertThat(manager.check(authentication, object).isGranted()).isFalse();
+	}
+
+}

+ 65 - 0
core/src/test/java/org/springframework/security/authorization/AuthorizationManagerTests.java

@@ -0,0 +1,65 @@
+/*
+ * Copyright 2002-2020 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.authorization;
+
+import org.junit.Test;
+
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.core.Authentication;
+
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+
+/**
+ * Tests for {@link AuthorizationManager}.
+ *
+ * @author Evgeniy Cheban
+ */
+public class AuthorizationManagerTests {
+
+	@Test
+	public void verifyWhenCheckReturnedGrantedDecisionThenPasses() {
+		AuthorizationManager<Object> manager = (a, o) -> new AuthorizationDecision(true);
+
+		Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_1", "ROLE_2");
+		Object object = new Object();
+
+		manager.verify(() -> authentication, object);
+	}
+
+	@Test
+	public void verifyWhenCheckReturnedNullThenPasses() {
+		AuthorizationManager<Object> manager = (a, o) -> null;
+
+		Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_1", "ROLE_2");
+		Object object = new Object();
+
+		manager.verify(() -> authentication, object);
+	}
+
+	@Test
+	public void verifyWhenCheckReturnedDeniedDecisionThenAccessDeniedException() {
+		AuthorizationManager<Object> manager = (a, o) -> new AuthorizationDecision(false);
+
+		Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_1", "ROLE_2");
+		Object object = new Object();
+
+		assertThatExceptionOfType(AccessDeniedException.class)
+				.isThrownBy(() -> manager.verify(() -> authentication, object)).withMessage("Access Denied");
+	}
+
+}

+ 69 - 0
web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java

@@ -0,0 +1,69 @@
+/*
+ * Copyright 2002-2020 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.web.access.intercept;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
+import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.util.Assert;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+/**
+ * An authorization filter that restricts access to the URL using
+ * {@link AuthorizationManager}.
+ *
+ * @author Evgeniy Cheban
+ */
+public class AuthorizationFilter extends OncePerRequestFilter {
+
+	private final AuthorizationManager<HttpServletRequest> authorizationManager;
+
+	/**
+	 * Creates an instance.
+	 * @param authorizationManager the {@link AuthorizationManager} to use
+	 */
+	public AuthorizationFilter(AuthorizationManager<HttpServletRequest> authorizationManager) {
+		Assert.notNull(authorizationManager, "authorizationManager cannot be null");
+		this.authorizationManager = authorizationManager;
+	}
+
+	@Override
+	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+			throws ServletException, IOException {
+
+		this.authorizationManager.verify(this::getAuthentication, request);
+		filterChain.doFilter(request, response);
+	}
+
+	private Authentication getAuthentication() {
+		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+		if (authentication == null) {
+			throw new AuthenticationCredentialsNotFoundException(
+					"An Authentication object was not found in the SecurityContext");
+		}
+		return authentication;
+	}
+
+}

+ 124 - 0
web/src/main/java/org/springframework/security/web/access/intercept/DelegatingAuthorizationManager.java

@@ -0,0 +1,124 @@
+/*
+ * Copyright 2002-2020 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.web.access.intercept;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.core.log.LogMessage;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher.MatchResult;
+import org.springframework.util.Assert;
+
+/**
+ * An {@link AuthorizationManager} which delegates to a specific
+ * {@link AuthorizationManager} based on a {@link RequestMatcher} evaluation.
+ *
+ * @author Evgeniy Cheban
+ */
+public final class DelegatingAuthorizationManager implements AuthorizationManager<HttpServletRequest> {
+
+	private final Log logger = LogFactory.getLog(getClass());
+
+	private final Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings;
+
+	private DelegatingAuthorizationManager(
+			Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings) {
+		Assert.notEmpty(mappings, "mappings cannot be empty");
+		this.mappings = mappings;
+	}
+
+	/**
+	 * Delegates to a specific {@link AuthorizationManager} based on a
+	 * {@link RequestMatcher} evaluation.
+	 * @param authentication the {@link Supplier} of the {@link Authentication} to check
+	 * @param request the {@link HttpServletRequest} to check
+	 * @return an {@link AuthorizationDecision}. If there is no {@link RequestMatcher}
+	 * matching the request, or the {@link AuthorizationManager} could not decide, then
+	 * null is returned
+	 */
+	@Override
+	public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) {
+		if (this.logger.isTraceEnabled()) {
+			this.logger.trace(LogMessage.format("Authorizing %s", request));
+		}
+		for (Map.Entry<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mapping : this.mappings
+				.entrySet()) {
+
+			RequestMatcher matcher = mapping.getKey();
+			MatchResult matchResult = matcher.matcher(request);
+			if (matchResult.isMatch()) {
+				AuthorizationManager<RequestAuthorizationContext> manager = mapping.getValue();
+				if (this.logger.isTraceEnabled()) {
+					this.logger.trace(LogMessage.format("Checking authorization on %s using %s", request, manager));
+				}
+				return manager.check(authentication,
+						new RequestAuthorizationContext(request, matchResult.getVariables()));
+			}
+		}
+		this.logger.trace("Abstaining since did not find matching RequestMatcher");
+		return null;
+	}
+
+	/**
+	 * Creates a builder for {@link DelegatingAuthorizationManager}.
+	 * @return the new {@link Builder} instance
+	 */
+	public static Builder builder() {
+		return new Builder();
+	}
+
+	/**
+	 * A builder for {@link DelegatingAuthorizationManager}.
+	 */
+	public static final class Builder {
+
+		private final Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings = new LinkedHashMap<>();
+
+		/**
+		 * Maps a {@link RequestMatcher} to an {@link AuthorizationManager}.
+		 * @param matcher the {@link RequestMatcher} to use
+		 * @param manager the {@link AuthorizationManager} to use
+		 * @return the {@link Builder} for further customizations
+		 */
+		public Builder add(RequestMatcher matcher, AuthorizationManager<RequestAuthorizationContext> manager) {
+			Assert.notNull(matcher, "matcher cannot be null");
+			Assert.notNull(manager, "manager cannot be null");
+			this.mappings.put(matcher, manager);
+			return this;
+		}
+
+		/**
+		 * Creates a {@link DelegatingAuthorizationManager} instance.
+		 * @return the {@link DelegatingAuthorizationManager} instance
+		 */
+		public DelegatingAuthorizationManager build() {
+			return new DelegatingAuthorizationManager(this.mappings);
+		}
+
+	}
+
+}

+ 72 - 0
web/src/main/java/org/springframework/security/web/access/intercept/RequestAuthorizationContext.java

@@ -0,0 +1,72 @@
+/*
+ * Copyright 2002-2020 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.web.access.intercept;
+
+import java.util.Collections;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * An {@link HttpServletRequest} authorization context.
+ *
+ * @author Evgeniy Cheban
+ */
+public final class RequestAuthorizationContext {
+
+	private final HttpServletRequest request;
+
+	private final Map<String, String> variables;
+
+	/**
+	 * Creates an instance.
+	 * @param request the {@link HttpServletRequest} to use
+	 */
+	public RequestAuthorizationContext(HttpServletRequest request) {
+		this(request, Collections.emptyMap());
+	}
+
+	/**
+	 * Creates an instance.
+	 * @param request the {@link HttpServletRequest} to use
+	 * @param variables a map containing key-value pairs representing extracted variable
+	 * names and variable values
+	 */
+	public RequestAuthorizationContext(HttpServletRequest request, Map<String, String> variables) {
+		this.request = request;
+		this.variables = variables;
+	}
+
+	/**
+	 * Returns the {@link HttpServletRequest}.
+	 * @return the {@link HttpServletRequest} to use
+	 */
+	public HttpServletRequest getRequest() {
+		return this.request;
+	}
+
+	/**
+	 * Returns the extracted variable values where the key is the variable name and the
+	 * value is the variable value.
+	 * @return a map containing key-value pairs representing extracted variable names and
+	 * variable values
+	 */
+	public Map<String, String> getVariables() {
+		return this.variables;
+	}
+
+}

+ 128 - 0
web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java

@@ -0,0 +1,128 @@
+/*
+ * Copyright 2002-2020 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.web.access.intercept;
+
+import java.util.function.Supplier;
+
+import javax.servlet.FilterChain;
+import javax.servlet.http.HttpServletRequest;
+
+import org.junit.After;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
+import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextImpl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.BDDMockito.willThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+
+/**
+ * Tests for {@link AuthorizationFilter}.
+ *
+ * @author Evgeniy Cheban
+ */
+public class AuthorizationFilterTests {
+
+	@After
+	public void tearDown() {
+		SecurityContextHolder.clearContext();
+	}
+
+	@Test
+	public void filterWhenAuthorizationManagerVerifyPassesThenNextFilter() throws Exception {
+		AuthorizationManager<HttpServletRequest> mockAuthorizationManager = mock(AuthorizationManager.class);
+		AuthorizationFilter filter = new AuthorizationFilter(mockAuthorizationManager);
+		TestingAuthenticationToken authenticationToken = new TestingAuthenticationToken("user", "password");
+
+		SecurityContext securityContext = new SecurityContextImpl();
+		securityContext.setAuthentication(authenticationToken);
+		SecurityContextHolder.setContext(securityContext);
+
+		MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path");
+		MockHttpServletResponse mockResponse = new MockHttpServletResponse();
+		FilterChain mockFilterChain = mock(FilterChain.class);
+
+		filter.doFilter(mockRequest, mockResponse, mockFilterChain);
+
+		ArgumentCaptor<Supplier<Authentication>> authenticationCaptor = ArgumentCaptor.forClass(Supplier.class);
+		verify(mockAuthorizationManager).verify(authenticationCaptor.capture(), eq(mockRequest));
+		Supplier<Authentication> authentication = authenticationCaptor.getValue();
+		assertThat(authentication.get()).isEqualTo(authenticationToken);
+
+		verify(mockFilterChain).doFilter(mockRequest, mockResponse);
+	}
+
+	@Test
+	public void filterWhenAuthorizationManagerVerifyThrowsAccessDeniedExceptionThenStopFilterChain() {
+		AuthorizationManager<HttpServletRequest> mockAuthorizationManager = mock(AuthorizationManager.class);
+		AuthorizationFilter filter = new AuthorizationFilter(mockAuthorizationManager);
+		TestingAuthenticationToken authenticationToken = new TestingAuthenticationToken("user", "password");
+
+		SecurityContext securityContext = new SecurityContextImpl();
+		securityContext.setAuthentication(authenticationToken);
+		SecurityContextHolder.setContext(securityContext);
+
+		MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path");
+		MockHttpServletResponse mockResponse = new MockHttpServletResponse();
+		FilterChain mockFilterChain = mock(FilterChain.class);
+
+		willThrow(new AccessDeniedException("Access Denied")).given(mockAuthorizationManager).verify(any(),
+				eq(mockRequest));
+
+		assertThatExceptionOfType(AccessDeniedException.class)
+				.isThrownBy(() -> filter.doFilter(mockRequest, mockResponse, mockFilterChain))
+				.withMessage("Access Denied");
+
+		ArgumentCaptor<Supplier<Authentication>> authenticationCaptor = ArgumentCaptor.forClass(Supplier.class);
+		verify(mockAuthorizationManager).verify(authenticationCaptor.capture(), eq(mockRequest));
+		Supplier<Authentication> authentication = authenticationCaptor.getValue();
+		assertThat(authentication.get()).isEqualTo(authenticationToken);
+
+		verifyNoInteractions(mockFilterChain);
+	}
+
+	@Test
+	public void filterWhenAuthenticationNullThenAuthenticationCredentialsNotFoundException() {
+		AuthorizationFilter filter = new AuthorizationFilter(AuthenticatedAuthorizationManager.authenticated());
+		MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path");
+		MockHttpServletResponse mockResponse = new MockHttpServletResponse();
+		FilterChain mockFilterChain = mock(FilterChain.class);
+
+		assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
+				.isThrownBy(() -> filter.doFilter(mockRequest, mockResponse, mockFilterChain))
+				.withMessage("An Authentication object was not found in the SecurityContext");
+
+		verifyNoInteractions(mockFilterChain);
+	}
+
+}

+ 84 - 0
web/src/test/java/org/springframework/security/web/access/intercept/DelegatingAuthorizationManagerTests.java

@@ -0,0 +1,84 @@
+/*
+ * Copyright 2002-2020 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.web.access.intercept;
+
+import java.util.function.Supplier;
+
+import org.junit.Test;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+
+/**
+ * Tests for {@link DelegatingAuthorizationManager}.
+ *
+ * @author Evgeniy Cheban
+ */
+public class DelegatingAuthorizationManagerTests {
+
+	@Test
+	public void buildWhenMappingsEmptyThenException() {
+		assertThatIllegalArgumentException().isThrownBy(() -> DelegatingAuthorizationManager.builder().build())
+				.withMessage("mappings cannot be empty");
+	}
+
+	@Test
+	public void addWhenMatcherNullThenException() {
+		assertThatIllegalArgumentException()
+				.isThrownBy(() -> DelegatingAuthorizationManager.builder()
+						.add(null, (a, o) -> new AuthorizationDecision(true)).build())
+				.withMessage("matcher cannot be null");
+	}
+
+	@Test
+	public void addWhenManagerNullThenException() {
+		assertThatIllegalArgumentException().isThrownBy(
+				() -> DelegatingAuthorizationManager.builder().add(new MvcRequestMatcher(null, "/grant"), null).build())
+				.withMessage("manager cannot be null");
+	}
+
+	@Test
+	public void checkWhenMultipleMappingsConfiguredThenDelegatesMatchingManager() {
+		DelegatingAuthorizationManager manager = DelegatingAuthorizationManager.builder()
+				.add(new MvcRequestMatcher(null, "/grant"), (a, o) -> new AuthorizationDecision(true))
+				.add(new MvcRequestMatcher(null, "/deny"), (a, o) -> new AuthorizationDecision(false))
+				.add(new MvcRequestMatcher(null, "/neutral"), (a, o) -> null).build();
+
+		Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
+
+		AuthorizationDecision grant = manager.check(authentication, new MockHttpServletRequest(null, "/grant"));
+		assertThat(grant).isNotNull();
+		assertThat(grant.isGranted()).isTrue();
+
+		AuthorizationDecision deny = manager.check(authentication, new MockHttpServletRequest(null, "/deny"));
+		assertThat(deny).isNotNull();
+		assertThat(deny.isGranted()).isFalse();
+
+		AuthorizationDecision neutral = manager.check(authentication, new MockHttpServletRequest(null, "/neutral"));
+		assertThat(neutral).isNull();
+
+		AuthorizationDecision abstain = manager.check(authentication, new MockHttpServletRequest(null, "/abstain"));
+		assertThat(abstain).isNull();
+	}
+
+}