Skip to content

Commit 0d6ef5f

Browse files
Add Max Sessions on WebFlux
Closes gh-6192
1 parent 93befd5 commit 0d6ef5f

File tree

30 files changed

+3342
-23
lines changed

30 files changed

+3342
-23
lines changed

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

+399-12
Large diffs are not rendered by default.

Diff for: config/src/main/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDsl.kt

+30
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,36 @@ class ServerHttpSecurityDsl(private val http: ServerHttpSecurity, private val in
682682
this.http.oidcLogout(oidcLogoutCustomizer)
683683
}
684684

685+
/**
686+
* Configures Session Management support.
687+
*
688+
* Example:
689+
*
690+
* ```
691+
* @Configuration
692+
* @EnableWebFluxSecurity
693+
* open class SecurityConfig {
694+
*
695+
* @Bean
696+
* open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
697+
* return http {
698+
* sessionManagement {
699+
* sessionConcurrency { }
700+
* }
701+
* }
702+
* }
703+
* }
704+
* ```
705+
*
706+
* @param sessionManagementConfig custom configuration to configure the Session Management
707+
* @since 6.3
708+
* @see [ServerSessionManagementDsl]
709+
*/
710+
fun sessionManagement(sessionManagementConfig: ServerSessionManagementDsl.() -> Unit) {
711+
val sessionManagementCustomizer = ServerSessionManagementDsl().apply(sessionManagementConfig).get()
712+
this.http.sessionManagement(sessionManagementCustomizer)
713+
}
714+
685715
/**
686716
* Apply all configurations to the provided [ServerHttpSecurity]
687717
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2002-2023 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.web.server
18+
19+
import org.springframework.security.core.session.ReactiveSessionRegistry
20+
import org.springframework.security.web.server.authentication.ServerMaximumSessionsExceededHandler
21+
import org.springframework.security.web.server.authentication.SessionLimit
22+
23+
/**
24+
* A Kotlin DSL to configure [ServerHttpSecurity] Session Concurrency support using idiomatic Kotlin code.
25+
*
26+
* @author Marcus da Coregio
27+
* @since 6.3
28+
*/
29+
@ServerSecurityMarker
30+
class ServerSessionConcurrencyDsl {
31+
var maximumSessions: SessionLimit? = null
32+
var maximumSessionsExceededHandler: ServerMaximumSessionsExceededHandler? = null
33+
var sessionRegistry: ReactiveSessionRegistry? = null
34+
35+
internal fun get(): (ServerHttpSecurity.SessionManagementSpec.ConcurrentSessionsSpec) -> Unit {
36+
return { sessionConcurrency ->
37+
maximumSessions?.also {
38+
sessionConcurrency.maximumSessions(maximumSessions!!)
39+
}
40+
maximumSessionsExceededHandler?.also {
41+
sessionConcurrency.maximumSessionsExceededHandler(maximumSessionsExceededHandler!!)
42+
}
43+
sessionRegistry?.also {
44+
sessionConcurrency.sessionRegistry(sessionRegistry!!)
45+
}
46+
}
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2002-2023 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.web.server
18+
19+
/**
20+
* A Kotlin DSL to configure [ServerHttpSecurity] Session Management using idiomatic Kotlin code.
21+
*
22+
* @author Marcus da Coregio
23+
* @since 6.3
24+
*/
25+
@ServerSecurityMarker
26+
class ServerSessionManagementDsl {
27+
private var sessionConcurrency: ((ServerHttpSecurity.SessionManagementSpec.ConcurrentSessionsSpec) -> Unit)? = null
28+
29+
/**
30+
* Enables Session Management support.
31+
*
32+
* Example:
33+
*
34+
* ```
35+
* @Configuration
36+
* @EnableWebFluxSecurity
37+
* open class SecurityConfig {
38+
*
39+
* @Bean
40+
* open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
41+
* return http {
42+
* sessionManagement {
43+
* sessionConcurrency {
44+
* maximumSessions = { authentication -> Mono.just(1) }
45+
* }
46+
* }
47+
* }
48+
* }
49+
* }
50+
* ```
51+
*
52+
* @param backChannelConfig custom configurations to configure OIDC 1.0 Back-Channel Logout support
53+
* @see [ServerOidcBackChannelLogoutDsl]
54+
*/
55+
fun sessionConcurrency(sessionConcurrencyConfig: ServerSessionConcurrencyDsl.() -> Unit) {
56+
this.sessionConcurrency = ServerSessionConcurrencyDsl().apply(sessionConcurrencyConfig).get()
57+
}
58+
59+
internal fun get(): (ServerHttpSecurity.SessionManagementSpec) -> Unit {
60+
return { sessionManagement ->
61+
sessionConcurrency?.also { sessionManagement.concurrentSessions(sessionConcurrency) }
62+
}
63+
}
64+
}

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

+8-2
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,11 @@
5656
import org.springframework.security.web.server.ServerRedirectStrategy;
5757
import org.springframework.security.web.server.WebFilterChainProxy;
5858
import org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilterTests;
59+
import org.springframework.security.web.server.authentication.DelegatingServerAuthenticationSuccessHandler;
5960
import org.springframework.security.web.server.authentication.HttpBasicServerAuthenticationEntryPoint;
6061
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
6162
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
63+
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
6264
import org.springframework.security.web.server.authentication.ServerX509AuthenticationConverter;
6365
import org.springframework.security.web.server.authentication.logout.DelegatingServerLogoutHandler;
6466
import org.springframework.security.web.server.authentication.logout.LogoutWebFilter;
@@ -592,6 +594,7 @@ public void postWhenServerXorCsrfTokenRequestAttributeHandlerThenOk() {
592594
}
593595

594596
@Test
597+
@SuppressWarnings("unchecked")
595598
public void shouldConfigureRequestCacheForOAuth2LoginAuthenticationEntryPointAndSuccessHandler() {
596599
ServerRequestCache requestCache = spy(new WebSessionServerRequestCache());
597600
ReactiveClientRegistrationRepository clientRegistrationRepository = mock(
@@ -613,8 +616,11 @@ public void shouldConfigureRequestCacheForOAuth2LoginAuthenticationEntryPointAnd
613616
OAuth2LoginAuthenticationWebFilter authenticationWebFilter = getWebFilter(securityFilterChain,
614617
OAuth2LoginAuthenticationWebFilter.class)
615618
.get();
616-
Object handler = ReflectionTestUtils.getField(authenticationWebFilter, "authenticationSuccessHandler");
617-
assertThat(ReflectionTestUtils.getField(handler, "requestCache")).isSameAs(requestCache);
619+
DelegatingServerAuthenticationSuccessHandler handler = (DelegatingServerAuthenticationSuccessHandler) ReflectionTestUtils
620+
.getField(authenticationWebFilter, "authenticationSuccessHandler");
621+
List<ServerAuthenticationSuccessHandler> delegates = (List<ServerAuthenticationSuccessHandler>) ReflectionTestUtils
622+
.getField(handler, "delegates");
623+
assertThat(ReflectionTestUtils.getField(delegates.get(0), "requestCache")).isSameAs(requestCache);
618624
}
619625

620626
@Test

0 commit comments

Comments
 (0)