Remove need for WebSecurityConfigurerAdapter
Closes gh-8804
This commit is contained in:
committed by
Eleftheria Stein-Kousathana
parent
5061ae9e79
commit
aeafe04260
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user