Skip to content

Commit 43e3763

Browse files
committed
Remove need for WebSecurityConfigurerAdapter
Closes gh-8804
1 parent f8f3302 commit 43e3763

File tree

8 files changed

+561
-10
lines changed

8 files changed

+561
-10
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/WebSecurityConfigurer.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,14 +23,17 @@
2323
import org.springframework.security.config.annotation.web.builders.WebSecurity;
2424
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
2525
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
26+
import org.springframework.security.web.SecurityFilterChain;
2627

2728
/**
2829
* Allows customization to the {@link WebSecurity}. In most instances users will use
29-
* {@link EnableWebSecurity} and create a {@link Configuration} that extends
30-
* {@link WebSecurityConfigurerAdapter} which will automatically be applied to the
31-
* {@link WebSecurity} by the {@link EnableWebSecurity} annotation.
30+
* {@link EnableWebSecurity} and either create a {@link Configuration} that extends
31+
* {@link WebSecurityConfigurerAdapter} or expose a {@link SecurityFilterChain} bean.
32+
* Both will automatically be applied to the {@link WebSecurity} by the
33+
* {@link EnableWebSecurity} annotation.
3234
*
3335
* @see WebSecurityConfigurerAdapter
36+
* @see SecurityFilterChain
3437
*
3538
* @author Rob Winch
3639
* @since 3.2

config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,8 @@ protected Filter performBuild() throws Exception {
283283
Assert.state(
284284
!securityFilterChainBuilders.isEmpty(),
285285
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
286-
+ "Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
286+
+ "Typically this is done by exposing a SecurityFilterChain bean "
287+
+ "or by adding a @Configuration that extends WebSecurityConfigurerAdapter. "
287288
+ "More advanced users can invoke "
288289
+ WebSecurity.class.getSimpleName()
289290
+ ".addSecurityFilterChainBuilder directly");

config/src/main/java/org/springframework/security/config/annotation/web/configuration/EnableWebSecurity.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -74,7 +74,8 @@
7474
@Documented
7575
@Import({ WebSecurityConfiguration.class,
7676
SpringWebMvcImportSelector.class,
77-
OAuth2ImportSelector.class })
77+
OAuth2ImportSelector.class,
78+
HttpSecurityConfiguration.class})
7879
@EnableGlobalAuthentication
7980
@Configuration
8081
public @interface EnableWebSecurity {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright 2002-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.web.configuration;
18+
19+
import org.springframework.beans.factory.annotation.Autowired;
20+
import org.springframework.context.ApplicationContext;
21+
import org.springframework.context.annotation.Bean;
22+
import org.springframework.context.annotation.Configuration;
23+
import org.springframework.context.annotation.Scope;
24+
import org.springframework.security.authentication.AuthenticationManager;
25+
import org.springframework.security.config.annotation.ObjectPostProcessor;
26+
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
27+
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
28+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
29+
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
30+
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
31+
32+
import java.util.HashMap;
33+
import java.util.Map;
34+
35+
import static org.springframework.security.config.Customizer.withDefaults;
36+
37+
/**
38+
* {@link Configuration} that exposes the {@link HttpSecurity} bean.
39+
*
40+
* @author Eleftheria Stein
41+
* @since 5.4
42+
*/
43+
@Configuration(proxyBeanMethods = false)
44+
class HttpSecurityConfiguration {
45+
private static final String BEAN_NAME_PREFIX = "org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.";
46+
private static final String HTTPSECURITY_BEAN_NAME = BEAN_NAME_PREFIX + "httpSecurity";
47+
48+
private ObjectPostProcessor<Object> objectPostProcessor;
49+
50+
private AuthenticationManager authenticationManager;
51+
52+
private AuthenticationConfiguration authenticationConfiguration;
53+
54+
private ApplicationContext context;
55+
56+
@Autowired
57+
public void setObjectPostProcessor(ObjectPostProcessor<Object> objectPostProcessor) {
58+
this.objectPostProcessor = objectPostProcessor;
59+
}
60+
61+
@Autowired(required = false)
62+
void setAuthenticationManager(AuthenticationManager authenticationManager) {
63+
this.authenticationManager = authenticationManager;
64+
}
65+
66+
@Autowired
67+
public void setAuthenticationConfiguration(
68+
AuthenticationConfiguration authenticationConfiguration) {
69+
this.authenticationConfiguration = authenticationConfiguration;
70+
}
71+
72+
@Autowired
73+
public void setApplicationContext(ApplicationContext context) {
74+
this.context = context;
75+
}
76+
77+
@Bean(HTTPSECURITY_BEAN_NAME)
78+
@Scope("prototype")
79+
public HttpSecurity httpSecurity() throws Exception {
80+
WebSecurityConfigurerAdapter.LazyPasswordEncoder passwordEncoder =
81+
new WebSecurityConfigurerAdapter.LazyPasswordEncoder(this.context);
82+
83+
AuthenticationManagerBuilder authenticationBuilder =
84+
new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(this.objectPostProcessor, passwordEncoder);
85+
authenticationBuilder.parentAuthenticationManager(authenticationManager());
86+
87+
HttpSecurity http = new HttpSecurity(objectPostProcessor, authenticationBuilder, createSharedObjects());
88+
http
89+
.csrf(withDefaults())
90+
.addFilter(new WebAsyncManagerIntegrationFilter())
91+
.exceptionHandling(withDefaults())
92+
.headers(withDefaults())
93+
.sessionManagement(withDefaults())
94+
.securityContext(withDefaults())
95+
.requestCache(withDefaults())
96+
.anonymous(withDefaults())
97+
.servletApi(withDefaults())
98+
.logout(withDefaults())
99+
.apply(new DefaultLoginPageConfigurer<>());
100+
101+
return http;
102+
}
103+
104+
private AuthenticationManager authenticationManager() throws Exception {
105+
if (this.authenticationManager != null) {
106+
return this.authenticationManager;
107+
} else {
108+
return this.authenticationConfiguration.getAuthenticationManager();
109+
}
110+
}
111+
112+
private Map<Class<?>, Object> createSharedObjects() {
113+
Map<Class<?>, Object> sharedObjects = new HashMap<>();
114+
sharedObjects.put(ApplicationContext.class, context);
115+
return sharedObjects;
116+
}
117+
}

config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.java

+28-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.security.config.annotation.web.configuration;
1717

18+
import java.util.Collections;
1819
import java.util.List;
1920
import java.util.Map;
2021
import javax.servlet.Filter;
@@ -43,7 +44,9 @@
4344
import org.springframework.security.context.DelegatingApplicationListener;
4445
import org.springframework.security.web.FilterChainProxy;
4546
import org.springframework.security.web.FilterInvocation;
47+
import org.springframework.security.web.SecurityFilterChain;
4648
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
49+
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
4750
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
4851

4952

@@ -70,6 +73,8 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
7073

7174
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
7275

76+
private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();
77+
7378
private ClassLoader beanClassLoader;
7479

7580
@Autowired(required = false)
@@ -95,12 +100,27 @@ public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler(
95100
public Filter springSecurityFilterChain() throws Exception {
96101
boolean hasConfigurers = webSecurityConfigurers != null
97102
&& !webSecurityConfigurers.isEmpty();
98-
if (!hasConfigurers) {
103+
boolean hasFilterChain = !securityFilterChains.isEmpty();
104+
if (hasConfigurers && hasFilterChain) {
105+
throw new IllegalStateException(
106+
"Found WebSecurityConfigurerAdapter as well as SecurityFilterChain." +
107+
"Please select just one.");
108+
}
109+
if (!hasConfigurers && !hasFilterChain) {
99110
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
100111
.postProcess(new WebSecurityConfigurerAdapter() {
101112
});
102113
webSecurity.apply(adapter);
103114
}
115+
for (SecurityFilterChain securityFilterChain : securityFilterChains) {
116+
webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
117+
for (Filter filter : securityFilterChain.getFilters()) {
118+
if (filter instanceof FilterSecurityInterceptor) {
119+
webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
120+
break;
121+
}
122+
}
123+
}
104124
return webSecurity.build();
105125
}
106126

@@ -158,6 +178,12 @@ public void setFilterChainProxySecurityConfigurer(
158178
this.webSecurityConfigurers = webSecurityConfigurers;
159179
}
160180

181+
@Autowired(required = false)
182+
void setFilterChains(List<SecurityFilterChain> securityFilterChains) {
183+
securityFilterChains.sort(AnnotationAwareOrderComparator.INSTANCE);
184+
this.securityFilterChains = securityFilterChains;
185+
}
186+
161187
@Bean
162188
public static BeanFactoryPostProcessor conversionServicePostProcessor() {
163189
return new RsaKeyConversionServicePostProcessor();

config/src/test/java/org/springframework/security/config/annotation/web/configuration/EnableWebSecurityTests.java

+28-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
2929
import org.springframework.security.core.Authentication;
3030
import org.springframework.security.core.annotation.AuthenticationPrincipal;
3131
import org.springframework.security.core.userdetails.PasswordEncodedUser;
32+
import org.springframework.security.web.SecurityFilterChain;
3233
import org.springframework.security.web.debug.DebugFilter;
3334
import org.springframework.test.web.servlet.MockMvc;
3435
import org.springframework.web.bind.annotation.GetMapping;
@@ -123,6 +124,32 @@ String principal(@AuthenticationPrincipal String principal) {
123124
}
124125
}
125126

127+
@Test
128+
public void securityFilterChainWhenEnableWebMvcThenAuthenticationPrincipalResolvable() throws Exception {
129+
this.spring.register(SecurityFilterChainAuthenticationPrincipalConfig.class).autowire();
130+
131+
this.mockMvc.perform(get("/").with(authentication(new TestingAuthenticationToken("user1", "password"))))
132+
.andExpect(content().string("user1"));
133+
}
134+
135+
@EnableWebSecurity
136+
@EnableWebMvc
137+
static class SecurityFilterChainAuthenticationPrincipalConfig {
138+
@Bean
139+
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
140+
return http.build();
141+
}
142+
143+
@RestController
144+
static class AuthController {
145+
146+
@GetMapping("/")
147+
String principal(@AuthenticationPrincipal String principal) {
148+
return principal;
149+
}
150+
}
151+
}
152+
126153
@Test
127154
public void enableWebSecurityWhenNoConfigurationAnnotationThenBeanProxyingEnabled() {
128155
this.spring.register(BeanProxyEnabledByDefaultConfig.class).autowire();

0 commit comments

Comments
 (0)