Переглянути джерело

Remove Direct Runtime Dependency on Access API

Issue gh-17847
Josh Cummings 2 тижнів тому
батько
коміт
3a1692f3c3

+ 3 - 4
config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterOrderRegistration.java

@@ -23,9 +23,7 @@ import java.util.Map;
 import jakarta.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.AuthenticationFilter;
 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@@ -78,7 +76,7 @@ final class FilterOrderRegistration {
 		Step order = new Step(INITIAL_ORDER, ORDER_STEP);
 		put(DisableEncodeUrlFilter.class, order.next());
 		put(ForceEagerSessionCreationFilter.class, order.next());
-		put(ChannelProcessingFilter.class, order.next());
+		this.filterToOrder.put("org.springframework.security.web.access.channel.ChannelProcessingFilter", order.next());
 		put(HttpsRedirectFilter.class, order.next());
 		order.next(); // gh-8105
 		put(WebAsyncManagerIntegrationFilter.class, order.next());
@@ -126,7 +124,8 @@ final class FilterOrderRegistration {
 				order.next());
 		put(SessionManagementFilter.class, order.next());
 		put(ExceptionTranslationFilter.class, order.next());
-		put(FilterSecurityInterceptor.class, order.next());
+		this.filterToOrder.put("org.springframework.security.web.access.intercept.FilterSecurityInterceptor",
+				order.next());
 		put(AuthorizationFilter.class, order.next());
 		put(SwitchUserFilter.class, order.next());
 	}

+ 94 - 21
config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java

@@ -18,6 +18,7 @@ package org.springframework.security.config.annotation.web.builders;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Supplier;
 
 import io.micrometer.observation.ObservationRegistry;
 import jakarta.servlet.Filter;
@@ -25,6 +26,8 @@ import jakarta.servlet.ServletContext;
 import jakarta.servlet.http.HttpServletRequest;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@@ -32,8 +35,11 @@ import org.springframework.beans.factory.ObjectProvider;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.core.ResolvableType;
+import org.springframework.expression.EvaluationContext;
 import org.springframework.security.access.PermissionEvaluator;
+import org.springframework.security.access.expression.AbstractSecurityExpressionHandler;
 import org.springframework.security.access.expression.SecurityExpressionHandler;
+import org.springframework.security.access.expression.SecurityExpressionOperations;
 import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
 import org.springframework.security.authorization.AuthorizationDecision;
 import org.springframework.security.authorization.AuthorizationManager;
@@ -46,6 +52,7 @@ import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
+import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.web.DefaultSecurityFilterChain;
 import org.springframework.security.web.FilterChainProxy;
@@ -58,7 +65,7 @@ import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEval
 import org.springframework.security.web.access.PathPatternRequestTransformer;
 import org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;
 import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
-import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
+import org.springframework.security.web.access.expression.DefaultHttpSecurityExpressionHandler;
 import org.springframework.security.web.access.intercept.AuthorizationFilter;
 import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
 import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
@@ -74,6 +81,7 @@ import org.springframework.security.web.util.matcher.AnyRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcherEntry;
 import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
 import org.springframework.web.context.ServletContextAware;
 import org.springframework.web.filter.DelegatingFilterProxy;
 
@@ -99,6 +107,9 @@ import org.springframework.web.filter.DelegatingFilterProxy;
 public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
 		implements SecurityBuilder<Filter>, ApplicationContextAware, ServletContextAware {
 
+	private static final boolean USING_ACCESS = ClassUtils
+		.isPresent("org.springframework.security.access.SecurityConfig", null);
+
 	private final Log logger = LogFactory.getLog(getClass());
 
 	private final List<RequestMatcher> ignoredRequests = new ArrayList<>();
@@ -122,9 +133,10 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
 
 	private HttpServletRequestTransformer privilegeEvaluatorRequestTransformer;
 
-	private DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
+	private DefaultHttpSecurityExpressionHandler defaultExpressionHandler = new DefaultHttpSecurityExpressionHandler();
 
-	private SecurityExpressionHandler<FilterInvocation> expressionHandler = this.defaultWebSecurityExpressionHandler;
+	private SecurityExpressionHandler<FilterInvocation> expressionHandler = new SecurityExpressionHandlerAdapter(
+			this.defaultExpressionHandler);
 
 	private Runnable postBuildAction = () -> {
 	};
@@ -240,7 +252,7 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
 
 	/**
 	 * Set the {@link SecurityExpressionHandler} to be used. If this is not specified,
-	 * then a {@link DefaultWebSecurityExpressionHandler} will be used.
+	 * then a {@link DefaultHttpSecurityExpressionHandler} will be used.
 	 * @param expressionHandler the {@link SecurityExpressionHandler} to use
 	 * @return the {@link WebSecurity} for further customizations
 	 */
@@ -361,19 +373,9 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
 			RequestMatcherDelegatingAuthorizationManager.Builder builder) {
 		boolean mappings = false;
 		for (Filter filter : securityFilterChain.getFilters()) {
-			if (filter instanceof FilterSecurityInterceptor securityInterceptor) {
-				DefaultWebInvocationPrivilegeEvaluator privilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
-						securityInterceptor);
-				privilegeEvaluator.setServletContext(this.servletContext);
-				AuthorizationManager<RequestAuthorizationContext> authorizationManager = (authentication, context) -> {
-					HttpServletRequest request = context.getRequest();
-					boolean result = privilegeEvaluator.isAllowed(request.getContextPath(), request.getRequestURI(),
-							request.getMethod(), authentication.get());
-					return new AuthorizationDecision(result);
-				};
-				builder.add(securityFilterChain::matches, authorizationManager);
-				mappings = true;
-				continue;
+			if (USING_ACCESS) {
+				mappings = AccessComponents.addAuthorizationManager(filter, this.servletContext, builder,
+						securityFilterChain);
 			}
 			if (filter instanceof AuthorizationFilter authorization) {
 				AuthorizationManager<HttpServletRequest> authorizationManager = authorization.getAuthorizationManager();
@@ -388,15 +390,14 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
 
 	@Override
 	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
-		this.defaultWebSecurityExpressionHandler.setApplicationContext(applicationContext);
+		this.defaultExpressionHandler.setApplicationContext(applicationContext);
 		try {
-			this.defaultWebSecurityExpressionHandler.setRoleHierarchy(applicationContext.getBean(RoleHierarchy.class));
+			this.defaultExpressionHandler.setRoleHierarchy(applicationContext.getBean(RoleHierarchy.class));
 		}
 		catch (NoSuchBeanDefinitionException ex) {
 		}
 		try {
-			this.defaultWebSecurityExpressionHandler
-				.setPermissionEvaluator(applicationContext.getBean(PermissionEvaluator.class));
+			this.defaultExpressionHandler.setPermissionEvaluator(applicationContext.getBean(PermissionEvaluator.class));
 		}
 		catch (NoSuchBeanDefinitionException ex) {
 		}
@@ -463,4 +464,76 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
 
 	}
 
+	@NullMarked
+	private static final class SecurityExpressionHandlerAdapter
+			extends AbstractSecurityExpressionHandler<FilterInvocation> {
+
+		private final AbstractSecurityExpressionHandler<RequestAuthorizationContext> delegate;
+
+		private SecurityExpressionHandlerAdapter(
+				AbstractSecurityExpressionHandler<RequestAuthorizationContext> delegate) {
+			this.delegate = delegate;
+		}
+
+		@Override
+		public EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,
+				FilterInvocation invocation) {
+			RequestAuthorizationContext context = new RequestAuthorizationContext(invocation.getRequest());
+			return this.delegate.createEvaluationContext(authentication, context);
+		}
+
+		@Override
+		protected SecurityExpressionOperations createSecurityExpressionRoot(@Nullable Authentication authentication,
+				FilterInvocation invocation) {
+			RequestAuthorizationContext context = new RequestAuthorizationContext(invocation.getRequest());
+			Object operations = this.delegate.createEvaluationContext(authentication, context)
+				.getRootObject()
+				.getValue();
+			Assert.isInstanceOf(SecurityExpressionOperations.class, operations,
+					"createEvaluationContext must have a SecurityExpressionOperations instance as its root");
+			return (SecurityExpressionOperations) operations;
+		}
+
+		@Override
+		public void setApplicationContext(ApplicationContext context) {
+			this.delegate.setApplicationContext(context);
+			super.setApplicationContext(context);
+		}
+
+		@Override
+		public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {
+			this.delegate.setPermissionEvaluator(permissionEvaluator);
+			super.setPermissionEvaluator(permissionEvaluator);
+		}
+
+		@Override
+		public void setRoleHierarchy(@Nullable RoleHierarchy roleHierarchy) {
+			this.delegate.setRoleHierarchy(roleHierarchy);
+			super.setRoleHierarchy(roleHierarchy);
+		}
+
+	}
+
+	private static final class AccessComponents {
+
+		private static boolean addAuthorizationManager(Filter filter, ServletContext servletContext,
+				RequestMatcherDelegatingAuthorizationManager.Builder builder, SecurityFilterChain securityFilterChain) {
+			if (filter instanceof FilterSecurityInterceptor securityInterceptor) {
+				DefaultWebInvocationPrivilegeEvaluator privilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
+						securityInterceptor);
+				privilegeEvaluator.setServletContext(servletContext);
+				AuthorizationManager<RequestAuthorizationContext> authorizationManager = (authentication, context) -> {
+					HttpServletRequest request = context.getRequest();
+					boolean result = privilegeEvaluator.isAllowed(request.getContextPath(), request.getRequestURI(),
+							request.getMethod(), authentication.get());
+					return new AuthorizationDecision(result);
+				};
+				builder.add(securityFilterChain::matches, authorizationManager);
+				return true;
+			}
+			return false;
+		}
+
+	}
+
 }

+ 13 - 1
config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurityFilterChainValidator.java

@@ -29,6 +29,7 @@ import org.springframework.security.web.UnreachableFilterChainException;
 import org.springframework.security.web.access.intercept.AuthorizationFilter;
 import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
 import org.springframework.security.web.util.matcher.AnyRequestMatcher;
+import org.springframework.util.ClassUtils;
 
 /**
  * A filter chain validator for filter chains built by {@link WebSecurity}
@@ -39,6 +40,9 @@ import org.springframework.security.web.util.matcher.AnyRequestMatcher;
  */
 final class WebSecurityFilterChainValidator implements FilterChainProxy.FilterChainValidator {
 
+	private static final boolean USING_ACCESS = ClassUtils
+		.isPresent("org.springframework.security.access.SecurityConfig", null);
+
 	private final Log logger = LogFactory.getLog(getClass());
 
 	@Override
@@ -93,7 +97,7 @@ final class WebSecurityFilterChainValidator implements FilterChainProxy.FilterCh
 				if (filter instanceof AuthorizationFilter) {
 					authorizationFilter = filter;
 				}
-				if (filter instanceof FilterSecurityInterceptor) {
+				if (USING_ACCESS && AccessComponents.isFilterSecurityInterceptor(filter)) {
 					filterSecurityInterceptor = filter;
 				}
 			}
@@ -110,4 +114,12 @@ final class WebSecurityFilterChainValidator implements FilterChainProxy.FilterCh
 		}
 	}
 
+	private static final class AccessComponents {
+
+		private static boolean isFilterSecurityInterceptor(Filter filter) {
+			return filter instanceof FilterSecurityInterceptor;
+		}
+
+	}
+
 }

+ 68 - 27
config/src/main/java/org/springframework/security/config/http/DefaultFilterChainValidator.java

@@ -56,9 +56,13 @@ import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
 import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
 import org.springframework.security.web.session.SessionManagementFilter;
 import org.springframework.security.web.util.matcher.AnyRequestMatcher;
+import org.springframework.util.ClassUtils;
 
 public class DefaultFilterChainValidator implements FilterChainProxy.FilterChainValidator {
 
+	private static final boolean USING_ACCESS = ClassUtils
+		.isPresent("org.springframework.security.access.SecurityConfig", null);
+
 	private static final Authentication TEST = new TestingAuthenticationToken("", "", Collections.emptyList());
 
 	private final Log logger = LogFactory.getLog(getClass());
@@ -120,7 +124,7 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
 				if (filter instanceof AuthorizationFilter) {
 					authorizationFilter = filter;
 				}
-				if (filter instanceof FilterSecurityInterceptor) {
+				if (USING_ACCESS && AccessComponents.isFilterSecurityInterceptor(filter)) {
 					filterSecurityInterceptor = filter;
 				}
 			}
@@ -138,7 +142,7 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
 	}
 
 	@SuppressWarnings({ "unchecked" })
-	private <F extends Filter> F getFilter(Class<F> type, List<Filter> filters) {
+	private static <F extends Filter> F getFilter(Class<F> type, List<Filter> filters) {
 		for (Filter f : filters) {
 			if (type.isAssignableFrom(f.getClass())) {
 				return (F) f;
@@ -158,7 +162,9 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
 		checkForDuplicates(SecurityContextHolderAwareRequestFilter.class, filters);
 		checkForDuplicates(JaasApiIntegrationFilter.class, filters);
 		checkForDuplicates(ExceptionTranslationFilter.class, filters);
-		checkForDuplicates(FilterSecurityInterceptor.class, filters);
+		if (USING_ACCESS) {
+			checkForDuplicates(AccessComponents.getFilterSecurityInterceptorClass(), filters);
+		}
 		checkForDuplicates(AuthorizationFilter.class, filters);
 	}
 
@@ -243,19 +249,11 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
 	}
 
 	private boolean checkLoginPageIsPublic(List<Filter> filters, HttpServletRequest loginRequest) {
-		FilterSecurityInterceptor authorizationInterceptor = getFilter(FilterSecurityInterceptor.class, filters);
-		if (authorizationInterceptor != null) {
-			FilterInvocationSecurityMetadataSource fids = authorizationInterceptor.getSecurityMetadataSource();
-			Collection<ConfigAttribute> attributes = fids.getAttributes(loginRequest);
-			if (attributes == null) {
-				this.logger.debug("No access attributes defined for login page URL");
-				if (authorizationInterceptor.isRejectPublicInvocations()) {
-					this.logger.warn("FilterSecurityInterceptor is configured to reject public invocations."
-							+ " Your login page may not be accessible.");
-				}
-				return true;
+		if (USING_ACCESS) {
+			Boolean isPublic = AccessComponents.checkLoginPageIsPublic(filters, loginRequest);
+			if (isPublic != null) {
+				return isPublic;
 			}
-			return false;
 		}
 		AuthorizationFilter authorizationFilter = getFilter(AuthorizationFilter.class, filters);
 		if (authorizationFilter != null) {
@@ -274,8 +272,60 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
 
 	private Supplier<Boolean> deriveAnonymousCheck(List<Filter> filters, HttpServletRequest loginRequest,
 			AnonymousAuthenticationToken token) {
-		FilterSecurityInterceptor authorizationInterceptor = getFilter(FilterSecurityInterceptor.class, filters);
-		if (authorizationInterceptor != null) {
+		if (USING_ACCESS) {
+			Supplier<Boolean> check = AccessComponents.getAnonymousCheck(filters, loginRequest, token);
+			if (check != null) {
+				return check;
+			}
+		}
+		AuthorizationFilter authorizationFilter = getFilter(AuthorizationFilter.class, filters);
+		if (authorizationFilter != null) {
+			return () -> {
+				AuthorizationManager<HttpServletRequest> authorizationManager = authorizationFilter
+					.getAuthorizationManager();
+				AuthorizationResult result = authorizationManager.authorize(() -> token, loginRequest);
+				return result != null && result.isGranted();
+			};
+		}
+		return () -> true;
+	}
+
+	private static final class AccessComponents {
+
+		private static final Log logger = LogFactory.getLog(DefaultFilterChainValidator.class);
+
+		private static boolean isFilterSecurityInterceptor(Filter filter) {
+			return filter instanceof FilterSecurityInterceptor;
+		}
+
+		private static Class<FilterSecurityInterceptor> getFilterSecurityInterceptorClass() {
+			return FilterSecurityInterceptor.class;
+		}
+
+		private static Boolean checkLoginPageIsPublic(List<Filter> filters, HttpServletRequest loginRequest) {
+			FilterSecurityInterceptor authorizationInterceptor = getFilter(FilterSecurityInterceptor.class, filters);
+			if (authorizationInterceptor == null) {
+				return null;
+			}
+			FilterInvocationSecurityMetadataSource fids = authorizationInterceptor.getSecurityMetadataSource();
+			Collection<ConfigAttribute> attributes = fids.getAttributes(loginRequest);
+			if (attributes == null) {
+				logger.debug("No access attributes defined for login page URL");
+				if (authorizationInterceptor.isRejectPublicInvocations()) {
+					logger.warn("FilterSecurityInterceptor is configured to reject public invocations."
+							+ " Your login page may not be accessible.");
+				}
+				return true;
+			}
+			return false;
+		}
+
+		private static Supplier<Boolean> getAnonymousCheck(List<Filter> filters, HttpServletRequest loginRequest,
+				AnonymousAuthenticationToken token) {
+			FilterSecurityInterceptor authorizationInterceptor = getFilter(FilterSecurityInterceptor.class, filters);
+			if (authorizationInterceptor == null) {
+				return null;
+			}
 			return () -> {
 				FilterInvocationSecurityMetadataSource source = authorizationInterceptor.getSecurityMetadataSource();
 				Collection<ConfigAttribute> attributes = source.getAttributes(loginRequest);
@@ -288,16 +338,7 @@ public class DefaultFilterChainValidator implements FilterChainProxy.FilterChain
 				}
 			};
 		}
-		AuthorizationFilter authorizationFilter = getFilter(AuthorizationFilter.class, filters);
-		if (authorizationFilter != null) {
-			return () -> {
-				AuthorizationManager<HttpServletRequest> authorizationManager = authorizationFilter
-					.getAuthorizationManager();
-				AuthorizationResult result = authorizationManager.authorize(() -> token, loginRequest);
-				return result != null && result.isGranted();
-			};
-		}
-		return () -> true;
+
 	}
 
 }

+ 1 - 2
config/src/test/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurationTests.java

@@ -60,7 +60,6 @@ import org.springframework.security.web.access.AuthorizationManagerWebInvocation
 import org.springframework.security.web.access.PathPatternRequestTransformer;
 import org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;
 import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
-import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
 import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.util.ClassUtils;
@@ -153,7 +152,7 @@ public class WebSecurityConfigurationTests {
 	public void loadConfigWhenDefaultSecurityExpressionHandlerThenDefaultIsRegistered() {
 		this.spring.register(WebSecurityExpressionHandlerDefaultsConfig.class).autowire();
 		assertThat(this.spring.getContext().getBean(SecurityExpressionHandler.class))
-			.isInstanceOf(DefaultWebSecurityExpressionHandler.class);
+			.isInstanceOf(AbstractSecurityExpressionHandler.class);
 	}
 
 	@Test

+ 4 - 2
test/src/test/java/org/springframework/security/test/context/showcase/WithMockUserParentTests.java

@@ -23,7 +23,8 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.test.context.showcase.service.HelloMessageService;
 import org.springframework.security.test.context.showcase.service.MessageService;
 import org.springframework.test.context.ContextConfiguration;
@@ -48,7 +49,8 @@ public class WithMockUserParentTests extends WithMockUserParent {
 	}
 
 	@Configuration
-	@EnableGlobalMethodSecurity(prePostEnabled = true)
+	@EnableMethodSecurity
+	@EnableWebSecurity
 	@ComponentScan(basePackageClasses = HelloMessageService.class)
 	static class Config {
 

+ 6 - 4
test/src/test/java/org/springframework/security/test/context/showcase/WithMockUserTests.java

@@ -31,7 +31,8 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.core.annotation.AliasFor;
 import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.test.context.showcase.service.HelloMessageService;
 import org.springframework.security.test.context.showcase.service.MessageService;
 import org.springframework.security.test.context.support.WithMockUser;
@@ -53,8 +54,8 @@ public class WithMockUserTests {
 
 	@Test
 	public void getMessageUnauthenticated() {
-		assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
-			.isThrownBy(() -> this.messageService.getMessage());
+		assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.messageService.getMessage())
+			.withRootCauseInstanceOf(AuthenticationCredentialsNotFoundException.class);
 	}
 
 	@Test
@@ -104,7 +105,8 @@ public class WithMockUserTests {
 		assertThat(message).contains("admin").contains("ADMIN").contains("ROLE_ADMIN");
 	}
 
-	@EnableGlobalMethodSecurity(prePostEnabled = true)
+	@EnableMethodSecurity
+	@EnableWebSecurity
 	@ComponentScan(basePackageClasses = HelloMessageService.class)
 	static class Config {
 

+ 6 - 4
test/src/test/java/org/springframework/security/test/context/showcase/WithUserDetailsTests.java

@@ -25,7 +25,8 @@ import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UserDetailsService;
@@ -51,8 +52,8 @@ public class WithUserDetailsTests {
 
 	@Test
 	public void getMessageUnauthenticated() {
-		assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
-			.isThrownBy(() -> this.messageService.getMessage());
+		assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.messageService.getMessage())
+			.withRootCauseInstanceOf(AuthenticationCredentialsNotFoundException.class);
 	}
 
 	@Test
@@ -84,7 +85,8 @@ public class WithUserDetailsTests {
 	}
 
 	@Configuration
-	@EnableGlobalMethodSecurity(prePostEnabled = true)
+	@EnableMethodSecurity
+	@EnableWebSecurity
 	@ComponentScan(basePackageClasses = HelloMessageService.class)
 	static class Config {