Remove need for WebSecurityConfigurerAdapter

Closes gh-8804
This commit is contained in:
Eleftheria Stein 2020-07-03 11:27:48 +02:00 committed by Eleftheria Stein-Kousathana
parent 5061ae9e79
commit aeafe04260
8 changed files with 561 additions and 10 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 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.
@ -23,14 +23,17 @@ import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.SecurityFilterChain;
/**
* Allows customization to the {@link WebSecurity}. In most instances users will use
* {@link EnableWebSecurity} and create a {@link Configuration} that extends
* {@link WebSecurityConfigurerAdapter} which will automatically be applied to the
* {@link WebSecurity} by the {@link EnableWebSecurity} annotation.
* {@link EnableWebSecurity} and either create a {@link Configuration} that extends
* {@link WebSecurityConfigurerAdapter} or expose a {@link SecurityFilterChain} bean.
* Both will automatically be applied to the {@link WebSecurity} by the
* {@link EnableWebSecurity} annotation.
*
* @see WebSecurityConfigurerAdapter
* @see SecurityFilterChain
*
* @author Rob Winch
* @since 3.2

View File

@ -283,7 +283,8 @@ public final class WebSecurity extends
Assert.state(
!securityFilterChainBuilders.isEmpty(),
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
+ "Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
+ "Typically this is done by exposing a SecurityFilterChain bean "
+ "or by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
+ "More advanced users can invoke "
+ WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@ -74,7 +74,8 @@ import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
@Documented
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class,
OAuth2ImportSelector.class })
OAuth2ImportSelector.class,
HttpSecurityConfiguration.class})
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {

View File

@ -0,0 +1,117 @@
/*
* 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.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
import java.util.HashMap;
import java.util.Map;
import static org.springframework.security.config.Customizer.withDefaults;
/**
* {@link Configuration} that exposes the {@link HttpSecurity} bean.
*
* @author Eleftheria Stein
* @since 5.4
*/
@Configuration(proxyBeanMethods = false)
class HttpSecurityConfiguration {
private static final String BEAN_NAME_PREFIX = "org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.";
private static final String HTTPSECURITY_BEAN_NAME = BEAN_NAME_PREFIX + "httpSecurity";
private ObjectPostProcessor<Object> objectPostProcessor;
private AuthenticationManager authenticationManager;
private AuthenticationConfiguration authenticationConfiguration;
private ApplicationContext context;
@Autowired
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
this.objectPostProcessor = objectPostProcessor;
}
@Autowired(required = false)
void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
@Autowired
public void setAuthenticationConfiguration(
AuthenticationConfiguration authenticationConfiguration) {
this.authenticationConfiguration = authenticationConfiguration;
}
@Autowired
public void setApplicationContext(ApplicationContext context) {
this.context = context;
}
@Bean(HTTPSECURITY_BEAN_NAME)
@Scope("prototype")
public HttpSecurity httpSecurity() throws Exception {
WebSecurityConfigurerAdapter.LazyPasswordEncoder passwordEncoder =
new WebSecurityConfigurerAdapter.LazyPasswordEncoder(this.context);
AuthenticationManagerBuilder authenticationBuilder =
new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(this.objectPostProcessor, passwordEncoder);
authenticationBuilder.parentAuthenticationManager(authenticationManager());
HttpSecurity http = new HttpSecurity(objectPostProcessor, authenticationBuilder, createSharedObjects());
http
.csrf(withDefaults())
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling(withDefaults())
.headers(withDefaults())
.sessionManagement(withDefaults())
.securityContext(withDefaults())
.requestCache(withDefaults())
.anonymous(withDefaults())
.servletApi(withDefaults())
.logout(withDefaults())
.apply(new DefaultLoginPageConfigurer<>());
return http;
}
private AuthenticationManager authenticationManager() throws Exception {
if (this.authenticationManager != null) {
return this.authenticationManager;
} else {
return this.authenticationConfiguration.getAuthenticationManager();
}
}
private Map<Class<?>, Object> createSharedObjects() {
Map<Class<?>, Object> sharedObjects = new HashMap<>();
sharedObjects.put(ApplicationContext.class, context);
return sharedObjects;
}
}

View File

@ -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.
@ -15,6 +15,7 @@
*/
package org.springframework.security.config.annotation.web.configuration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
@ -43,7 +44,9 @@ import org.springframework.security.config.crypto.RsaKeyConversionServicePostPro
import org.springframework.security.context.DelegatingApplicationListener;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
@ -70,6 +73,8 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();
private ClassLoader beanClassLoader;
@Autowired(required = false)
@ -95,12 +100,27 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
boolean hasFilterChain = !securityFilterChains.isEmpty();
if (hasConfigurers && hasFilterChain) {
throw new IllegalStateException(
"Found WebSecurityConfigurerAdapter as well as SecurityFilterChain." +
"Please select just one.");
}
if (!hasConfigurers && !hasFilterChain) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
for (SecurityFilterChain securityFilterChain : securityFilterChains) {
webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
for (Filter filter : securityFilterChain.getFilters()) {
if (filter instanceof FilterSecurityInterceptor) {
webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
break;
}
}
}
return webSecurity.build();
}
@ -158,6 +178,12 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
this.webSecurityConfigurers = webSecurityConfigurers;
}
@Autowired(required = false)
void setFilterChains(List<SecurityFilterChain> securityFilterChains) {
securityFilterChains.sort(AnnotationAwareOrderComparator.INSTANCE);
this.securityFilterChains = securityFilterChains;
}
@Bean
public static BeanFactoryPostProcessor conversionServicePostProcessor() {
return new RsaKeyConversionServicePostProcessor();

View File

@ -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.
@ -29,6 +29,7 @@ import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.debug.DebugFilter;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.GetMapping;
@ -123,6 +124,32 @@ public class EnableWebSecurityTests {
}
}
@Test
public void securityFilterChainWhenEnableWebMvcThenAuthenticationPrincipalResolvable() throws Exception {
this.spring.register(SecurityFilterChainAuthenticationPrincipalConfig.class).autowire();
this.mockMvc.perform(get("/").with(authentication(new TestingAuthenticationToken("user1", "password"))))
.andExpect(content().string("user1"));
}
@EnableWebSecurity
@EnableWebMvc
static class SecurityFilterChainAuthenticationPrincipalConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.build();
}
@RestController
static class AuthController {
@GetMapping("/")
String principal(@AuthenticationPrincipal String principal) {
return principal;
}
}
}
@Test
public void enableWebSecurityWhenNoConfigurationAnnotationThenBeanProxyingEnabled() {
this.spring.register(BeanProxyEnabledByDefaultConfig.class).autowire();

View File

@ -0,0 +1,248 @@
/*
* 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.configuration;
import com.google.common.net.HttpHeaders;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.Callable;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
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.asyncDispatch;
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.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Tests for {@link HttpSecurityConfiguration}.
*
* @author Eleftheria Stein
*/
public class HttpSecurityConfigurationTests {
@Rule
public final SpringTestRule spring = new SpringTestRule();
@Autowired
private MockMvc mockMvc;
@Test
public void postWhenDefaultFilterChainBeanThenRespondsWithForbidden() throws Exception {
this.spring.register(DefaultWithFilterChainConfig.class)
.autowire();
this.mockMvc.perform(post("/"))
.andExpect(status().isForbidden());
}
@Test
public void getWhenDefaultFilterChainBeanThenDefaultHeadersInResponse() throws Exception {
this.spring.register(DefaultWithFilterChainConfig.class).autowire();
MvcResult mvcResult = this.mockMvc.perform(get("/").secure(true))
.andExpect(header().string(HttpHeaders.X_CONTENT_TYPE_OPTIONS, "nosniff"))
.andExpect(header().string(HttpHeaders.X_FRAME_OPTIONS, XFrameOptionsHeaderWriter.XFrameOptionsMode.DENY.name()))
.andExpect(header().string(HttpHeaders.STRICT_TRANSPORT_SECURITY, "max-age=31536000 ; includeSubDomains"))
.andExpect(header().string(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, max-age=0, must-revalidate"))
.andExpect(header().string(HttpHeaders.EXPIRES, "0"))
.andExpect(header().string(HttpHeaders.PRAGMA, "no-cache"))
.andExpect(header().string(HttpHeaders.X_XSS_PROTECTION, "1; mode=block"))
.andReturn();
assertThat(mvcResult.getResponse().getHeaderNames()).containsExactlyInAnyOrder(
HttpHeaders.X_CONTENT_TYPE_OPTIONS, HttpHeaders.X_FRAME_OPTIONS, HttpHeaders.STRICT_TRANSPORT_SECURITY,
HttpHeaders.CACHE_CONTROL, HttpHeaders.EXPIRES, HttpHeaders.PRAGMA, HttpHeaders.X_XSS_PROTECTION);
}
@Test
public void logoutWhenDefaultFilterChainBeanThenCreatesDefaultLogoutEndpoint() throws Exception {
this.spring.register(DefaultWithFilterChainConfig.class).autowire();
this.mockMvc.perform(post("/logout").with(csrf()))
.andExpect(redirectedUrl("/login?logout"));
}
@Test
public void loadConfigWhenDefaultConfigThenWebAsyncManagerIntegrationFilterAdded() throws Exception {
this.spring.register(DefaultWithFilterChainConfig.class, NameController.class).autowire();
MvcResult mvcResult = this.mockMvc.perform(get("/name").with(user("Bob")))
.andExpect(request().asyncStarted())
.andReturn();
this.mockMvc.perform(asyncDispatch(mvcResult))
.andExpect(status().isOk())
.andExpect(content().string("Bob"));
}
@RestController
static class NameController {
@GetMapping("/name")
public Callable<String> name() {
return () -> SecurityContextHolder.getContext().getAuthentication().getName();
}
}
@EnableWebSecurity
static class DefaultWithFilterChainConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.build();
}
}
@Test
public void getWhenDefaultFilterChainBeanThenAnonymousPermitted() throws Exception {
this.spring.register(AuthorizeRequestsConfig.class, UserDetailsConfig.class, BaseController.class).autowire();
this.mockMvc.perform(get("/"))
.andExpect(status().isOk());
}
@EnableWebSecurity
static class AuthorizeRequestsConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeRequests(authorize -> authorize
.anyRequest().permitAll()
)
.build();
}
}
@Test
public void authenticateWhenDefaultFilterChainBeanThenSessionIdChanges() throws Exception {
this.spring.register(SecurityEnabledConfig.class, UserDetailsConfig.class).autowire();
MockHttpSession session = new MockHttpSession();
String sessionId = session.getId();
MvcResult result =
this.mockMvc.perform(post("/login")
.param("username", "user")
.param("password", "password")
.session(session)
.with(csrf()))
.andReturn();
assertThat(result.getRequest().getSession(false).getId()).isNotEqualTo(sessionId);
}
@Test
public void authenticateWhenDefaultFilterChainBeanThenRedirectsToSavedRequest() throws Exception {
this.spring.register(SecurityEnabledConfig.class, UserDetailsConfig.class).autowire();
MockHttpSession session = (MockHttpSession)
this.mockMvc.perform(get("/messages"))
.andReturn().getRequest().getSession();
this.mockMvc.perform(post("/login")
.param("username", "user")
.param("password", "password")
.session(session)
.with(csrf()))
.andExpect(redirectedUrl("http://localhost/messages"));
}
@Test
public void authenticateWhenDefaultFilterChainBeanThenRolePrefixIsSet() throws Exception {
this.spring.register(SecurityEnabledConfig.class, UserDetailsConfig.class, UserController.class).autowire();
this.mockMvc.perform(get("/user")
.with(authentication(new TestingAuthenticationToken("user", "password", "ROLE_USER"))))
.andExpect(status().isOk());
}
@Test
public void loginWhenUsingDefaultsThenDefaultLoginPageGenerated() throws Exception {
this.spring.register(SecurityEnabledConfig.class).autowire();
this.mockMvc.perform(get("/login"))
.andExpect(status().isOk());
}
@EnableWebSecurity
static class SecurityEnabledConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.build();
}
}
@Configuration
static class UserDetailsConfig {
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
@RestController
static class BaseController {
@GetMapping("/")
public void index() {
}
}
@RestController
static class UserController {
@GetMapping("/user")
public void user(HttpServletRequest request) {
if (!request.isUserInRole("USER")) {
throw new AccessDeniedException("This resource is only available to users");
}
}
}
}

View File

@ -170,6 +170,76 @@ public class WebSecurityConfigurationTests {
}
}
@Test
public void loadConfigWhenSecurityFilterChainsHaveOrderThenFilterChainsOrdered() {
this.spring.register(SortedSecurityFilterChainConfig.class).autowire();
FilterChainProxy filterChainProxy = this.spring.getContext().getBean(FilterChainProxy.class);
List<SecurityFilterChain> filterChains = filterChainProxy.getFilterChains();
assertThat(filterChains).hasSize(4);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "");
request.setServletPath("/role1/**");
assertThat(filterChains.get(0).matches(request)).isTrue();
request.setServletPath("/role2/**");
assertThat(filterChains.get(1).matches(request)).isTrue();
request.setServletPath("/role3/**");
assertThat(filterChains.get(2).matches(request)).isTrue();
request.setServletPath("/**");
assertThat(filterChains.get(3).matches(request)).isTrue();
}
@EnableWebSecurity
@Import(AuthenticationTestConfiguration.class)
static class SortedSecurityFilterChainConfig {
@Order(1)
@Bean
SecurityFilterChain filterChain1(HttpSecurity http) throws Exception {
return http
.antMatcher("/role1/**")
.authorizeRequests(authorize -> authorize
.anyRequest().hasRole("1")
)
.build();
}
@Order(2)
@Bean
SecurityFilterChain filterChain2(HttpSecurity http) throws Exception {
return http
.antMatcher("/role2/**")
.authorizeRequests(authorize -> authorize
.anyRequest().hasRole("2")
)
.build();
}
@Order(3)
@Bean
SecurityFilterChain filterChain3(HttpSecurity http) throws Exception {
return http
.antMatcher("/role3/**")
.authorizeRequests(authorize -> authorize
.anyRequest().hasRole("3")
)
.build();
}
@Bean
SecurityFilterChain filterChain4(HttpSecurity http) throws Exception {
return http
.authorizeRequests(authorize -> authorize
.anyRequest().hasRole("4")
)
.build();
}
}
@Test
public void loadConfigWhenWebSecurityConfigurersHaveSameOrderThenThrowBeanCreationException() {
Throwable thrown = catchThrowable(() -> this.spring.register(DuplicateOrderConfig.class).autowire());
@ -372,6 +442,26 @@ public class WebSecurityConfigurationTests {
}
}
@Test
public void loadConfigWhenSecurityFilterChainBeanThenDefaultWebInvocationPrivilegeEvaluatorIsRegistered() {
this.spring.register(AuthorizeRequestsFilterChainConfig.class).autowire();
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
.isInstanceOf(DefaultWebInvocationPrivilegeEvaluator.class);
}
@EnableWebSecurity
static class AuthorizeRequestsFilterChainConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
)
.build();
}
}
// SEC-2303
@Test
public void loadConfigWhenDefaultSecurityExpressionHandlerThenBeanResolverSet() throws Exception {
@ -499,4 +589,42 @@ public class WebSecurityConfigurationTests {
}
}
}
@Test
public void loadConfigWhenBothAdapterAndFilterChainConfiguredThenException() {
Throwable thrown = catchThrowable(() -> this.spring.register(AdapterAndFilterChainConfig.class).autowire());
assertThat(thrown).isInstanceOf(BeanCreationException.class)
.hasRootCauseExactlyInstanceOf(IllegalStateException.class)
.hasMessageContaining("Found WebSecurityConfigurerAdapter as well as SecurityFilterChain.");
}
@EnableWebSecurity
@Import(AuthenticationTestConfiguration.class)
static class AdapterAndFilterChainConfig {
@Order(1)
@Configuration
static class WebConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/config/**")
.authorizeRequests(authorize -> authorize
.anyRequest().permitAll()
);
}
}
@Order(2)
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.antMatcher("/filter/**")
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
)
.build();
}
}
}