Skip to content

Commit f95aed8

Browse files
authored
feat: Auto-configure a SecurityContextHolderStrategy bean (#22745)
This is the first step towards #21401 and completes the migration from VaadinWebSecurity (now removed) to the recently introduced VaadinSecurityConfigurer in #21373. It avoids the need to manually import VaadinAwareSecurityContextHolderStrategyConfiguration by removing the class and include the strategy bean definition in SpringSecurityAutoConfiguration with an additional SmartInitializingSingleton that sets the provided strategy bean statically in SecurityContextHolder. Removing VaadinAwareSecurityContextHolderStrategyConfiguration is a minor breaking change since applications that currently import that configuration manually with @import(VaadinAwareSecurityContextHolderStrategyConfiguration.class) must remove the explicit import. Runtime functionality is not affected since applications providing their own custom strategy bean will override the auto-configured bean and the custom strategy will be picked up by the SmartInitializingSingleton to be set statically. Note: a deprecation cycle of VaadinAwareSecurityContextHolderStrategyConfiguration is not feasible since it's not allowed to keep two configurations providing the same conditional bean. An option could to deprecate and make it no-op to avoid compiler errors when importing the configuration (but this might just create some confusion).
1 parent bade885 commit f95aed8

File tree

13 files changed

+45
-89
lines changed

13 files changed

+45
-89
lines changed

flow-tests/vaadin-spring-tests/test-spring-security-flow-methodsecurity/src/main/java/com/vaadin/flow/spring/flowsecurity/SecurityConfig.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
import org.springframework.context.annotation.Bean;
2323
import org.springframework.context.annotation.Configuration;
24-
import org.springframework.context.annotation.Import;
2524
import org.springframework.context.annotation.Profile;
2625
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
2726
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
@@ -43,7 +42,6 @@
4342
import com.vaadin.flow.spring.flowsecurity.data.UserInfo;
4443
import com.vaadin.flow.spring.flowsecurity.service.UserInfoService;
4544
import com.vaadin.flow.spring.flowsecurity.views.LoginView;
46-
import com.vaadin.flow.spring.security.VaadinAwareSecurityContextHolderStrategyConfiguration;
4745

4846
import static com.vaadin.flow.spring.flowsecurity.service.UserInfoService.ROLE_ADMIN;
4947
import static com.vaadin.flow.spring.security.VaadinSecurityConfigurer.vaadin;
@@ -52,7 +50,6 @@
5250
@EnableMethodSecurity(prePostEnabled = false, jsr250Enabled = true, securedEnabled = true)
5351
@Configuration
5452
@Profile("default")
55-
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
5653
public class SecurityConfig {
5754

5855
private final UserInfoService userInfoService;
@@ -85,8 +82,7 @@ public String getLogoutSuccessUrl() {
8582
}
8683

8784
@Bean
88-
SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http)
89-
throws Exception {
85+
SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http) {
9086
http.authorizeHttpRequests(auth -> auth
9187

9288
.requestMatchers(

flow-tests/vaadin-spring-tests/test-spring-security-flow-routepathaccesschecker/src/main/java/com/vaadin/flow/spring/flowsecurity/SecurityConfig.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import org.springframework.beans.factory.annotation.Autowired;
2323
import org.springframework.context.annotation.Bean;
2424
import org.springframework.context.annotation.Configuration;
25-
import org.springframework.context.annotation.Import;
2625
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
2726
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
2827
import org.springframework.security.core.authority.SimpleGrantedAuthority;
@@ -41,14 +40,12 @@
4140
import com.vaadin.flow.spring.flowsecurity.service.UserInfoService;
4241
import com.vaadin.flow.spring.flowsecurity.views.LoginView;
4342
import com.vaadin.flow.spring.security.NavigationAccessControlConfigurer;
44-
import com.vaadin.flow.spring.security.VaadinAwareSecurityContextHolderStrategyConfiguration;
4543

4644
import static com.vaadin.flow.spring.flowsecurity.service.UserInfoService.ROLE_ADMIN;
4745
import static com.vaadin.flow.spring.security.VaadinSecurityConfigurer.vaadin;
4846

4947
@EnableWebSecurity
5048
@Configuration
51-
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
5249
public class SecurityConfig {
5350

5451
@Autowired
@@ -97,8 +94,7 @@ public String getLogoutSuccessUrl() {
9794
}
9895

9996
@Bean
100-
SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http)
101-
throws Exception {
97+
SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http) {
10298
http.authorizeHttpRequests(cfg -> cfg
10399
.requestMatchers("/admin-only/**", "/admin")
104100
.hasAnyRole(ROLE_ADMIN).requestMatchers("/private")

flow-tests/vaadin-spring-tests/test-spring-security-flow-standalone-routepathaccesschecker/src/main/java/com/vaadin/flow/spring/flowsecurity/SecurityConfig.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import com.vaadin.flow.spring.security.RequestUtil;
5252
import com.vaadin.flow.spring.security.SpringAccessPathChecker;
5353
import com.vaadin.flow.spring.security.UidlRedirectStrategy;
54+
import com.vaadin.flow.spring.security.VaadinSavedRequestAwareAuthenticationSuccessHandler;
5455

5556
import static com.vaadin.flow.spring.flowsecurity.service.UserInfoService.ROLE_ADMIN;
5657

@@ -153,7 +154,9 @@ public SecurityFilterChain webFilterChain(HttpSecurity http,
153154
});
154155

155156
// Custom login page with form authentication
156-
http.formLogin(cfg -> cfg.loginPage("/my/login/page").permitAll());
157+
http.formLogin(cfg -> cfg.loginPage("/my/login/page").successHandler(
158+
new VaadinSavedRequestAwareAuthenticationSuccessHandler())
159+
.permitAll());
157160
DefaultSecurityFilterChain filterChain = http.build();
158161
// Test application uses AuthenticationContext, configure it with
159162
// the logout handlers

flow-tests/vaadin-spring-tests/test-spring-security-flow-standalone-routepathaccesschecker/src/test/java/com/vaadin/flow/spring/flowsecurity/AppViewIT.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import org.hamcrest.CoreMatchers;
2828
import org.hamcrest.MatcherAssert;
2929
import org.junit.Assert;
30-
import org.junit.Ignore;
3130
import org.junit.Test;
3231
import org.openqa.selenium.WebDriver;
3332

@@ -117,11 +116,6 @@ public void redirect_to_private_view_after_login() {
117116
}
118117

119118
@Test
120-
@Ignore("""
121-
Requires VaadinAwareSecurityContextHolderStrategyConfiguration that
122-
in a custom Spring Security configuration without Vaadin helpers might not be imported.
123-
Leaving the test here just as a template in case of future improvements.
124-
""")
125119
public void redirect_to_private_view_after_navigation_and_login() {
126120
open("");
127121
navigateTo("private", false);
@@ -286,11 +280,6 @@ public void navigate_in_thread_without_access() {
286280
}
287281

288282
@Test
289-
@Ignore("""
290-
Requires VaadinAwareSecurityContextHolderStrategyConfiguration that
291-
in a custom Spring Security configuration without Vaadin helpers might not be imported.
292-
Leaving the test here just as a template in case of future improvements.
293-
""")
294283
public void navigate_in_thread_with_access() {
295284
open(LOGIN_PATH);
296285
loginAdmin();

flow-tests/vaadin-spring-tests/test-spring-security-flow-standalone-routepathaccesschecker/src/test/java/com/vaadin/flow/spring/flowsecurity/UIAccessContextIT.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package com.vaadin.flow.spring.flowsecurity;
1717

1818
import org.junit.Assert;
19-
import org.junit.Ignore;
2019
import org.junit.Test;
2120
import org.openqa.selenium.WebDriver;
2221

@@ -28,11 +27,6 @@
2827
public class UIAccessContextIT extends AbstractIT {
2928

3029
@Test
31-
@Ignore("""
32-
Requires VaadinAwareSecurityContextHolderStrategyConfiguration that
33-
in a custom Spring Security configuration without Vaadin helpers might not be imported.
34-
Leaving the test here just as a template in case of future improvements.
35-
""")
3630
public void securityContextSetForUIAccess() throws Exception {
3731
String expectedUserBalance = "Hello John the User, your bank account balance is $10000.00.";
3832
String expectedAdminBalance = "Hello Emma the Admin, your bank account balance is $200000.00.";

flow-tests/vaadin-spring-tests/test-spring-security-flow-themes-contextpath/src/main/java/com/vaadin/flow/spring/flowthemessecuritycontextpath/SecurityConfig.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,20 @@
1717

1818
import org.springframework.context.annotation.Bean;
1919
import org.springframework.context.annotation.Configuration;
20-
import org.springframework.context.annotation.Import;
2120
import org.springframework.context.annotation.Profile;
2221
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
2322
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
2423
import org.springframework.security.web.SecurityFilterChain;
2524

26-
import com.vaadin.flow.spring.security.VaadinAwareSecurityContextHolderStrategyConfiguration;
27-
2825
import static com.vaadin.flow.spring.security.VaadinSecurityConfigurer.vaadin;
2926

3027
@EnableWebSecurity
3128
@Configuration
3229
@Profile("default")
33-
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
3430
public class SecurityConfig {
3531

3632
@Bean
37-
SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http)
38-
throws Exception {
33+
SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http) {
3934
http.with(vaadin(), cfg -> {
4035
});
4136
return http.build();

flow-tests/vaadin-spring-tests/test-spring-security-flow/src/main/java/com/vaadin/flow/spring/flowsecurity/SecurityConfig.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,13 @@
2525

2626
import org.springframework.context.annotation.Bean;
2727
import org.springframework.context.annotation.Configuration;
28-
import org.springframework.context.annotation.DependsOn;
29-
import org.springframework.context.annotation.Import;
3028
import org.springframework.context.annotation.Profile;
3129
import org.springframework.http.HttpMethod;
3230
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
3331
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
3432
import org.springframework.security.core.Authentication;
3533
import org.springframework.security.core.authority.SimpleGrantedAuthority;
34+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
3635
import org.springframework.security.core.userdetails.User;
3736
import org.springframework.security.core.userdetails.UserDetails;
3837
import org.springframework.security.core.userdetails.UsernameNotFoundException;
@@ -50,15 +49,13 @@
5049
import com.vaadin.flow.spring.flowsecurity.service.UserInfoService;
5150
import com.vaadin.flow.spring.flowsecurity.views.LoginView;
5251
import com.vaadin.flow.spring.security.UidlRedirectStrategy;
53-
import com.vaadin.flow.spring.security.VaadinAwareSecurityContextHolderStrategyConfiguration;
5452

5553
import static com.vaadin.flow.spring.flowsecurity.service.UserInfoService.ROLE_ADMIN;
5654
import static com.vaadin.flow.spring.security.VaadinSecurityConfigurer.vaadin;
5755

5856
@EnableWebSecurity
5957
@Configuration
6058
@Profile("default")
61-
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
6259
public class SecurityConfig {
6360

6461
private final UserInfoService userInfoService;
@@ -99,8 +96,7 @@ public String getLogoutSuccessUrl() {
9996
}
10097

10198
@Bean
102-
SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http)
103-
throws Exception {
99+
SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http) {
104100
http.authorizeHttpRequests(auth -> auth
105101
.requestMatchers("/admin-only/**").hasAnyRole(ROLE_ADMIN)
106102
.requestMatchers("/public/**", "/error").permitAll()
@@ -171,9 +167,10 @@ public UserDetails loadUserByUsername(String username)
171167
}
172168

173169
@Bean
174-
@DependsOn("VaadinSecurityContextHolderStrategy")
175-
public SwitchUserFilter switchUserFilter() {
170+
public SwitchUserFilter switchUserFilter(
171+
SecurityContextHolderStrategy securityContextHolderStrategy) {
176172
SwitchUserFilter filter = new SwitchUserFilter();
173+
filter.setSecurityContextHolderStrategy(securityContextHolderStrategy);
177174
filter.setUserDetailsService(userDetailsService());
178175
filter.setSwitchUserMatcher(PathPatternRequestMatcher
179176
.pathPattern(HttpMethod.GET, "/impersonate"));

flow-tests/vaadin-spring-tests/test-spring-security-webicons-urlmapping/src/main/java/com/vaadin/flow/spring/flowsecurity/SecurityConfig.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,20 @@
1717

1818
import org.springframework.context.annotation.Bean;
1919
import org.springframework.context.annotation.Configuration;
20-
import org.springframework.context.annotation.Import;
2120
import org.springframework.context.annotation.Profile;
2221
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
2322
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
2423
import org.springframework.security.web.SecurityFilterChain;
2524

26-
import com.vaadin.flow.spring.security.VaadinAwareSecurityContextHolderStrategyConfiguration;
27-
2825
import static com.vaadin.flow.spring.security.VaadinSecurityConfigurer.vaadin;
2926

3027
@EnableWebSecurity
3128
@Configuration
3229
@Profile("default")
33-
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
3430
public class SecurityConfig {
3531

3632
@Bean
37-
SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http)
38-
throws Exception {
33+
SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http) {
3934
http.with(vaadin(), cfg -> cfg.loginView(LoginView.class));
4035
return http.build();
4136
}

flow-tests/vaadin-spring-tests/test-spring-security-webicons/src/main/java/com/vaadin/flow/spring/flowsecurity/SecurityConfig.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,20 @@
1717

1818
import org.springframework.context.annotation.Bean;
1919
import org.springframework.context.annotation.Configuration;
20-
import org.springframework.context.annotation.Import;
2120
import org.springframework.context.annotation.Profile;
2221
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
2322
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
2423
import org.springframework.security.web.SecurityFilterChain;
2524

26-
import com.vaadin.flow.spring.security.VaadinAwareSecurityContextHolderStrategyConfiguration;
27-
2825
import static com.vaadin.flow.spring.security.VaadinSecurityConfigurer.vaadin;
2926

3027
@EnableWebSecurity
3128
@Configuration
3229
@Profile("default")
33-
@Import(VaadinAwareSecurityContextHolderStrategyConfiguration.class)
3430
public class SecurityConfig {
3531

3632
@Bean
37-
SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http)
38-
throws Exception {
33+
SecurityFilterChain vaadinSecurityFilterChain(HttpSecurity http) {
3934
http.with(vaadin(), cfg -> cfg.loginView(LoginView.class));
4035
return http.build();
4136
}

vaadin-spring/src/main/java/com/vaadin/flow/spring/SpringSecurityAutoConfiguration.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,17 @@
1818
import java.util.List;
1919
import java.util.Optional;
2020

21+
import org.springframework.beans.factory.SmartInitializingSingleton;
22+
import org.springframework.boot.autoconfigure.AutoConfiguration;
2123
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2224
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2325
import org.springframework.boot.context.properties.EnableConfigurationProperties;
2426
import org.springframework.context.annotation.Bean;
25-
import org.springframework.context.annotation.Configuration;
2627
import org.springframework.context.annotation.Lazy;
2728
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
2829
import org.springframework.security.config.core.GrantedAuthorityDefaults;
30+
import org.springframework.security.core.context.SecurityContextHolder;
31+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
2932
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
3033

3134
import com.vaadin.flow.server.auth.AccessAnnotationChecker;
@@ -40,6 +43,7 @@
4043
import com.vaadin.flow.spring.security.RequestUtil;
4144
import com.vaadin.flow.spring.security.SpringAccessPathChecker;
4245
import com.vaadin.flow.spring.security.SpringNavigationAccessControl;
46+
import com.vaadin.flow.spring.security.VaadinAwareSecurityContextHolderStrategy;
4347
import com.vaadin.flow.spring.security.VaadinDefaultRequestCache;
4448
import com.vaadin.flow.spring.security.VaadinRolePrefixHolder;
4549

@@ -49,7 +53,7 @@
4953
* @author Vaadin Ltd
5054
*
5155
*/
52-
@Configuration(proxyBeanMethods = false)
56+
@AutoConfiguration
5357
@ConditionalOnClass(WebSecurityCustomizer.class)
5458
@EnableConfigurationProperties(VaadinConfigurationProperties.class)
5559
public class SpringSecurityAutoConfiguration {
@@ -209,4 +213,16 @@ public VaadinRolePrefixHolder vaadinRolePrefixHolder(
209213
AuthenticationContext authenticationContext() {
210214
return new AuthenticationContext();
211215
}
216+
217+
@Bean
218+
@ConditionalOnMissingBean
219+
SecurityContextHolderStrategy vaadinAwareSecurityContextHolderStrategy() {
220+
return new VaadinAwareSecurityContextHolderStrategy();
221+
}
222+
223+
@Bean
224+
SmartInitializingSingleton securityContextHolderStrategyInitializer(
225+
SecurityContextHolderStrategy strategy) {
226+
return () -> SecurityContextHolder.setContextHolderStrategy(strategy);
227+
}
212228
}

0 commit comments

Comments
 (0)