Skip to content

Commit 8773c79

Browse files
eddumelendezjzheaux
authored andcommitted
Allow to set default securityContextRepository for each authentication mechanisms
Fixes gh-7249
1 parent 5e98b92 commit 8773c79

File tree

4 files changed

+118
-19
lines changed

4 files changed

+118
-19
lines changed

config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ public class ServerHttpSecurity {
268268

269269
private ReactiveAuthenticationManager authenticationManager;
270270

271-
private ServerSecurityContextRepository securityContextRepository = new WebSessionServerSecurityContextRepository();
271+
private ServerSecurityContextRepository securityContextRepository;
272272

273273
private ServerAuthenticationEntryPoint authenticationEntryPoint;
274274

@@ -346,7 +346,7 @@ private ServerWebExchangeMatcher getSecurityMatcher() {
346346
}
347347

348348
/**
349-
* The strategy used with {@code ReactorContextWebFilter}. It does not impact how the {@code SecurityContext} is
349+
* The strategy used with {@code ReactorContextWebFilter}. It does impact how the {@code SecurityContext} is
350350
* saved which is configured on a per {@link AuthenticationWebFilter} basis.
351351
* @param securityContextRepository the repository to use
352352
* @return the {@link ServerHttpSecurity} to continue configuring
@@ -971,7 +971,7 @@ public class OAuth2LoginSpec {
971971

972972
private ReactiveAuthenticationManager authenticationManager;
973973

974-
private ServerSecurityContextRepository securityContextRepository = new WebSessionServerSecurityContextRepository();
974+
private ServerSecurityContextRepository securityContextRepository;
975975

976976
private ServerAuthenticationConverter authenticationConverter;
977977

@@ -2254,9 +2254,7 @@ public SecurityWebFilterChain build() {
22542254
this.headers.configure(this);
22552255
}
22562256
WebFilter securityContextRepositoryWebFilter = securityContextRepositoryWebFilter();
2257-
if (securityContextRepositoryWebFilter != null) {
2258-
this.webFilters.add(securityContextRepositoryWebFilter);
2259-
}
2257+
this.webFilters.add(securityContextRepositoryWebFilter);
22602258
if (this.httpsRedirectSpec != null) {
22612259
this.httpsRedirectSpec.configure(this);
22622260
}
@@ -2273,18 +2271,42 @@ public SecurityWebFilterChain build() {
22732271
if (this.httpBasic.authenticationManager == null) {
22742272
this.httpBasic.authenticationManager(this.authenticationManager);
22752273
}
2274+
if (this.httpBasic.securityContextRepository != null) {
2275+
this.httpBasic.securityContextRepository(this.httpBasic.securityContextRepository);
2276+
}
2277+
else if (this.securityContextRepository != null) {
2278+
this.httpBasic.securityContextRepository(this.securityContextRepository);
2279+
}
2280+
else {
2281+
this.httpBasic.securityContextRepository(NoOpServerSecurityContextRepository.getInstance());
2282+
}
22762283
this.httpBasic.configure(this);
22772284
}
22782285
if (this.formLogin != null) {
22792286
if (this.formLogin.authenticationManager == null) {
22802287
this.formLogin.authenticationManager(this.authenticationManager);
22812288
}
2282-
if (this.securityContextRepository != null) {
2289+
if (this.formLogin.securityContextRepository != null) {
2290+
this.formLogin.securityContextRepository(this.formLogin.securityContextRepository);
2291+
}
2292+
else if (this.securityContextRepository != null) {
22832293
this.formLogin.securityContextRepository(this.securityContextRepository);
22842294
}
2295+
else {
2296+
this.formLogin.securityContextRepository(new WebSessionServerSecurityContextRepository());
2297+
}
22852298
this.formLogin.configure(this);
22862299
}
22872300
if (this.oauth2Login != null) {
2301+
if (this.oauth2Login.securityContextRepository != null) {
2302+
this.oauth2Login.securityContextRepository(this.oauth2Login.securityContextRepository);
2303+
}
2304+
else if (this.securityContextRepository != null) {
2305+
this.oauth2Login.securityContextRepository(this.securityContextRepository);
2306+
}
2307+
else {
2308+
this.oauth2Login.securityContextRepository(new WebSessionServerSecurityContextRepository());
2309+
}
22882310
this.oauth2Login.configure(this);
22892311
}
22902312
if (this.resourceServer != null) {
@@ -2379,10 +2401,8 @@ public static ServerHttpSecurity http() {
23792401
}
23802402

23812403
private WebFilter securityContextRepositoryWebFilter() {
2382-
ServerSecurityContextRepository repository = this.securityContextRepository;
2383-
if (repository == null) {
2384-
return null;
2385-
}
2404+
ServerSecurityContextRepository repository = this.securityContextRepository == null ?
2405+
new WebSessionServerSecurityContextRepository() : this.securityContextRepository;
23862406
WebFilter result = new ReactorContextWebFilter(repository);
23872407
return new OrderedWebFilter(result, SecurityWebFiltersOrder.REACTOR_CONTEXT.getOrder());
23882408
}
@@ -2774,7 +2794,7 @@ private RequestCacheSpec() {}
27742794
public class HttpBasicSpec {
27752795
private ReactiveAuthenticationManager authenticationManager;
27762796

2777-
private ServerSecurityContextRepository securityContextRepository = NoOpServerSecurityContextRepository.getInstance();
2797+
private ServerSecurityContextRepository securityContextRepository;
27782798

27792799
private ServerAuthenticationEntryPoint entryPoint = new HttpBasicServerAuthenticationEntryPoint();
27802800

@@ -2846,9 +2866,7 @@ protected void configure(ServerHttpSecurity http) {
28462866
this.authenticationManager);
28472867
authenticationFilter.setAuthenticationFailureHandler(new ServerAuthenticationEntryPointFailureHandler(this.entryPoint));
28482868
authenticationFilter.setAuthenticationConverter(new ServerHttpBasicAuthenticationConverter());
2849-
if (this.securityContextRepository != null) {
2850-
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
2851-
}
2869+
authenticationFilter.setSecurityContextRepository(this.securityContextRepository);
28522870
http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.HTTP_BASIC);
28532871
}
28542872

@@ -2869,7 +2887,7 @@ public class FormLoginSpec {
28692887

28702888
private ReactiveAuthenticationManager authenticationManager;
28712889

2872-
private ServerSecurityContextRepository securityContextRepository = new WebSessionServerSecurityContextRepository();
2890+
private ServerSecurityContextRepository securityContextRepository;
28732891

28742892
private ServerAuthenticationEntryPoint authenticationEntryPoint;
28752893

@@ -2966,7 +2984,7 @@ public FormLoginSpec authenticationFailureHandler(ServerAuthenticationFailureHan
29662984

29672985
/**
29682986
* The {@link ServerSecurityContextRepository} used to save the {@code Authentication}. Defaults to
2969-
* {@link NoOpServerSecurityContextRepository}. For the {@code SecurityContext} to be loaded on subsequent
2987+
* {@link WebSessionServerSecurityContextRepository}. For the {@code SecurityContext} to be loaded on subsequent
29702988
* requests the {@link ReactorContextWebFilter} must be configured to be able to load the value (they are not
29712989
* implicitly linked).
29722990
*

config/src/test/java/org/springframework/security/config/web/server/FormLoginTests.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,15 @@
2626
import org.springframework.security.authentication.ReactiveAuthenticationManager;
2727
import org.springframework.security.authentication.TestingAuthenticationToken;
2828
import org.springframework.security.config.annotation.web.reactive.ServerHttpSecurityConfigurationBuilder;
29+
import org.springframework.security.core.Authentication;
30+
import org.springframework.security.core.context.SecurityContext;
31+
import org.springframework.security.core.context.SecurityContextImpl;
2932
import org.springframework.security.htmlunit.server.WebTestClientHtmlUnitDriverBuilder;
3033
import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
3134
import org.springframework.security.web.server.SecurityWebFilterChain;
3235
import org.springframework.security.web.server.WebFilterChainProxy;
3336
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;
37+
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
3438
import org.springframework.security.web.server.csrf.CsrfToken;
3539
import org.springframework.stereotype.Controller;
3640
import org.springframework.test.web.reactive.server.WebTestClient;
@@ -44,12 +48,15 @@
4448
import static org.assertj.core.api.Assertions.assertThatThrownBy;
4549
import static org.mockito.ArgumentMatchers.any;
4650
import static org.mockito.BDDMockito.given;
51+
import static org.mockito.Mockito.atLeastOnce;
4752
import static org.mockito.Mockito.mock;
53+
import static org.mockito.Mockito.verify;
4854
import static org.mockito.Mockito.verifyZeroInteractions;
4955
import static org.springframework.security.config.Customizer.withDefaults;
5056

5157
/**
5258
* @author Rob Winch
59+
* @author Eddú Meléndez
5360
* @since 5.0
5461
*/
5562
public class FormLoginTests {
@@ -272,6 +279,50 @@ public void customAuthenticationManager() {
272279
verifyZeroInteractions(defaultAuthenticationManager);
273280
}
274281

282+
@Test
283+
public void formLoginSecurityContextRepository() {
284+
ServerSecurityContextRepository defaultSecContextRepository = mock(ServerSecurityContextRepository.class);
285+
ServerSecurityContextRepository formLoginSecContextRepository = mock(ServerSecurityContextRepository.class);
286+
287+
TestingAuthenticationToken token = new TestingAuthenticationToken("rob", "rob", "ROLE_USER");
288+
289+
given(defaultSecContextRepository.save(any(), any())).willReturn(Mono.empty());
290+
given(defaultSecContextRepository.load(any())).willReturn(authentication(token));
291+
given(formLoginSecContextRepository.save(any(), any())).willReturn(Mono.empty());
292+
given(formLoginSecContextRepository.load(any())).willReturn(authentication(token));
293+
294+
SecurityWebFilterChain securityWebFilter = this.http
295+
.authorizeExchange()
296+
.anyExchange().authenticated()
297+
.and()
298+
.securityContextRepository(defaultSecContextRepository)
299+
.formLogin()
300+
.securityContextRepository(formLoginSecContextRepository)
301+
.and()
302+
.build();
303+
304+
WebTestClient webTestClient = WebTestClientBuilder
305+
.bindToWebFilters(securityWebFilter)
306+
.build();
307+
308+
WebDriver driver = WebTestClientHtmlUnitDriverBuilder
309+
.webTestClientSetup(webTestClient)
310+
.build();
311+
312+
DefaultLoginPage loginPage = DefaultLoginPage.to(driver)
313+
.assertAt();
314+
315+
HomePage homePage = loginPage.loginForm()
316+
.username("user")
317+
.password("password")
318+
.submit(HomePage.class);
319+
320+
homePage.assertAt();
321+
322+
verify(defaultSecContextRepository, atLeastOnce()).load(any());
323+
verify(formLoginSecContextRepository).save(any(), any());
324+
}
325+
275326
public static class CustomLoginPage {
276327

277328
private WebDriver driver;
@@ -501,4 +552,10 @@ public Mono<String> login(ServerWebExchange exchange) {
501552
+ "</html>");
502553
}
503554
}
555+
556+
Mono<SecurityContext> authentication(Authentication authentication) {
557+
SecurityContext context = new SecurityContextImpl();
558+
context.setAuthentication(authentication);
559+
return Mono.just(context);
560+
}
504561
}

config/src/test/java/org/springframework/security/config/web/server/OAuth2LoginTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,7 @@ public void oauth2LoginWhenCustomBeansThenUsed() {
428428

429429
ServerSecurityContextRepository securityContextRepository = config.securityContextRepository;
430430
when(securityContextRepository.save(any(), any())).thenReturn(Mono.empty());
431+
when(securityContextRepository.load(any())).thenReturn(authentication(token));
431432

432433
Map<String, Object> additionalParameters = new HashMap<>();
433434
additionalParameters.put(OidcParameterNames.ID_TOKEN, "id-token");

config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575

7676
/**
7777
* @author Rob Winch
78+
* @author Eddú Meléndez
7879
* @since 5.0
7980
*/
8081
@RunWith(MockitoJUnitRunner.class)
@@ -117,7 +118,6 @@ public void defaults() {
117118
public void basic() {
118119
given(this.authenticationManager.authenticate(any())).willReturn(Mono.just(new TestingAuthenticationToken("rob", "rob", "ROLE_USER", "ROLE_ADMIN")));
119120

120-
this.http.securityContextRepository(new WebSessionServerSecurityContextRepository());
121121
this.http.httpBasic();
122122
this.http.authenticationManager(this.authenticationManager);
123123
ServerHttpSecurity.AuthorizeExchangeSpec authorize = this.http.authorizeExchange();
@@ -137,6 +137,30 @@ public void basic() {
137137
assertThat(result.getResponseCookies().getFirst("SESSION")).isNull();
138138
}
139139

140+
@Test
141+
public void basicWithGlobalWebSessionServerSecurityContextRepository() {
142+
given(this.authenticationManager.authenticate(any())).willReturn(Mono.just(new TestingAuthenticationToken("rob", "rob", "ROLE_USER", "ROLE_ADMIN")));
143+
144+
this.http.securityContextRepository(new WebSessionServerSecurityContextRepository());
145+
this.http.httpBasic();
146+
this.http.authenticationManager(this.authenticationManager);
147+
ServerHttpSecurity.AuthorizeExchangeSpec authorize = this.http.authorizeExchange();
148+
authorize.anyExchange().authenticated();
149+
150+
WebTestClient client = buildClient();
151+
152+
EntityExchangeResult<String> result = client.get()
153+
.uri("/")
154+
.headers(headers -> headers.setBasicAuth("rob", "rob"))
155+
.exchange()
156+
.expectStatus().isOk()
157+
.expectHeader().valueMatches(HttpHeaders.CACHE_CONTROL, ".+")
158+
.expectBody(String.class).consumeWith(b -> assertThat(b.getResponseBody()).isEqualTo("ok"))
159+
.returnResult();
160+
161+
assertThat(result.getResponseCookies().getFirst("SESSION")).isNotNull();
162+
}
163+
140164
@Test
141165
public void basicWhenNoCredentialsThenUnauthorized() {
142166
this.http.authorizeExchange().anyExchange().authenticated();
@@ -256,7 +280,6 @@ public void getWhenAnonymousConfiguredThenAuthenticationIsAnonymous() throws Exc
256280
public void basicWithAnonymous() {
257281
given(this.authenticationManager.authenticate(any())).willReturn(Mono.just(new TestingAuthenticationToken("rob", "rob", "ROLE_USER", "ROLE_ADMIN")));
258282

259-
this.http.securityContextRepository(new WebSessionServerSecurityContextRepository());
260283
this.http.httpBasic().and().anonymous();
261284
this.http.authenticationManager(this.authenticationManager);
262285
ServerHttpSecurity.AuthorizeExchangeSpec authorize = this.http.authorizeExchange();

0 commit comments

Comments
 (0)