Remove need for WebSecurityConfigurerAdapter
Closes gh-8804
This commit is contained in:
parent
5061ae9e79
commit
aeafe04260
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue