Skip to content

Commit 33ba292

Browse files
committed
Resource Server w/ SecurityReactorContextSubscriber
Fixes gh-7423
1 parent e6d40e8 commit 33ba292

File tree

5 files changed

+39
-161
lines changed

5 files changed

+39
-161
lines changed

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

+11-10
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,34 @@
1515
*/
1616
package org.springframework.security.config.annotation.web.configuration;
1717

18+
import java.util.LinkedHashSet;
19+
import java.util.Set;
20+
1821
import org.springframework.context.annotation.ImportSelector;
1922
import org.springframework.core.type.AnnotationMetadata;
2023
import org.springframework.util.ClassUtils;
2124

22-
import java.util.ArrayList;
23-
import java.util.List;
24-
2525
/**
2626
* Used by {@link EnableWebSecurity} to conditionally import:
2727
*
2828
* <ul>
2929
* <li>{@link OAuth2ClientConfiguration} when the {@code spring-security-oauth2-client} module is present on the classpath</li>
30-
* <li>{@link SecurityReactorContextConfiguration} when the {@code spring-webflux} and {@code spring-security-oauth2-client} module is present on the classpath</li>
31-
* <li>{@link OAuth2ResourceServerConfiguration} when the {@code spring-security-oauth2-resource-server} module is present on the classpath</li>
30+
* <li>{@link SecurityReactorContextConfiguration} when either the {@code spring-security-oauth2-client} or
31+
* {@code spring-security-oauth2-resource-server} module as well as the {@code spring-webflux} module
32+
* are present on the classpath</li>
3233
* </ul>
3334
*
3435
* @author Joe Grandja
3536
* @author Josh Cummings
3637
* @since 5.1
3738
* @see OAuth2ClientConfiguration
3839
* @see SecurityReactorContextConfiguration
39-
* @see OAuth2ResourceServerConfiguration
4040
*/
4141
final class OAuth2ImportSelector implements ImportSelector {
4242

4343
@Override
4444
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
45-
List<String> imports = new ArrayList<>();
45+
Set<String> imports = new LinkedHashSet<>();
4646

4747
boolean oauth2ClientPresent = ClassUtils.isPresent(
4848
"org.springframework.security.oauth2.client.registration.ClientRegistration", getClass().getClassLoader());
@@ -56,9 +56,10 @@ public String[] selectImports(AnnotationMetadata importingClassMetadata) {
5656
imports.add("org.springframework.security.config.annotation.web.configuration.SecurityReactorContextConfiguration");
5757
}
5858

59-
if (ClassUtils.isPresent(
60-
"org.springframework.security.oauth2.server.resource.BearerTokenError", getClass().getClassLoader())) {
61-
imports.add("org.springframework.security.config.annotation.web.configuration.OAuth2ResourceServerConfiguration");
59+
boolean oauth2ResourceServerPresent = ClassUtils.isPresent(
60+
"org.springframework.security.oauth2.server.resource.BearerTokenError", getClass().getClassLoader());
61+
if (webfluxPresent && oauth2ResourceServerPresent) {
62+
imports.add("org.springframework.security.config.annotation.web.configuration.SecurityReactorContextConfiguration");
6263
}
6364

6465
return imports.toArray(new String[0]);

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

-144
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@
4444
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
4545

4646
/**
47-
* Tests for {@link OAuth2ResourceServerConfiguration}.
47+
* Tests for applications of {@link SecurityReactorContextConfiguration} in resource servers.
4848
*
4949
* @author Josh Cummings
5050
*/
51-
public class OAuth2ResourceServerConfigurationTests {
51+
public class SecurityReactorContextConfigurationResourceServerTests {
5252
@Rule
5353
public final SpringTestRule spring = new SpringTestRule();
5454

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/reactive/function/client/ServletBearerExchangeFilterFunction.java

+15-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.security.oauth2.server.resource.web.reactive.function.client;
1818

19+
import java.util.Map;
20+
1921
import reactor.core.publisher.Mono;
2022
import reactor.util.context.Context;
2123

@@ -56,6 +58,9 @@
5658
public final class ServletBearerExchangeFilterFunction
5759
implements ExchangeFilterFunction {
5860

61+
static final String SECURITY_REACTOR_CONTEXT_ATTRIBUTES_KEY =
62+
"org.springframework.security.SECURITY_CONTEXT_ATTRIBUTES";
63+
5964
/**
6065
* {@inheritDoc}
6166
*/
@@ -76,8 +81,16 @@ private Mono<AbstractOAuth2Token> oauth2Token() {
7681
}
7782

7883
private Mono<Authentication> currentAuthentication(Context ctx) {
79-
Authentication authentication = ctx.getOrDefault(Authentication.class, null);
80-
return Mono.justOrEmpty(authentication);
84+
return Mono.justOrEmpty(getAttribute(ctx, Authentication.class));
85+
}
86+
87+
private <T> T getAttribute(Context ctx, Class<T> clazz) {
88+
// NOTE: SecurityReactorContextConfiguration.SecurityReactorContextSubscriber adds this key
89+
if (!ctx.hasKey(SECURITY_REACTOR_CONTEXT_ATTRIBUTES_KEY)) {
90+
return null;
91+
}
92+
Map<Class<T>, T> attributes = ctx.get(SECURITY_REACTOR_CONTEXT_ATTRIBUTES_KEY);
93+
return attributes.get(clazz);
8194
}
8295

8396
private ClientRequest bearer(ClientRequest request, AbstractOAuth2Token token) {

oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/reactive/function/client/ServletBearerExchangeFilterFunctionTests.java

+11-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.time.Duration;
2121
import java.time.Instant;
2222
import java.util.Collections;
23+
import java.util.HashMap;
2324
import java.util.Map;
2425

2526
import org.junit.Test;
@@ -37,6 +38,7 @@
3738

3839
import static org.assertj.core.api.Assertions.assertThat;
3940
import static org.springframework.http.HttpMethod.GET;
41+
import static org.springframework.security.oauth2.server.resource.web.reactive.function.client.ServletBearerExchangeFilterFunction.SECURITY_REACTOR_CONTEXT_ATTRIBUTES_KEY;
4042

4143
/**
4244
* Tests for {@link ServletBearerExchangeFilterFunction}
@@ -80,7 +82,7 @@ public void filterWhenAuthenticatedWithOtherTokenThenAuthorizationHeaderNull() {
8082
.build();
8183

8284
this.function.filter(request, this.exchange)
83-
.subscriberContext(Context.of(Authentication.class, token))
85+
.subscriberContext(context(token))
8486
.block();
8587

8688
assertThat(this.exchange.getRequest().headers().getFirst(HttpHeaders.AUTHORIZATION))
@@ -93,7 +95,7 @@ public void filterWhenAuthenticatedThenAuthorizationHeader() {
9395
.build();
9496

9597
this.function.filter(request, this.exchange)
96-
.subscriberContext(Context.of(Authentication.class, this.authentication))
98+
.subscriberContext(context(this.authentication))
9799
.block();
98100

99101
assertThat(this.exchange.getRequest().headers().getFirst(HttpHeaders.AUTHORIZATION))
@@ -107,10 +109,16 @@ public void filterWhenExistingAuthorizationThenSingleAuthorizationHeader() {
107109
.build();
108110

109111
this.function.filter(request, this.exchange)
110-
.subscriberContext(Context.of(Authentication.class, this.authentication))
112+
.subscriberContext(context(this.authentication))
111113
.block();
112114

113115
HttpHeaders headers = this.exchange.getRequest().headers();
114116
assertThat(headers.get(HttpHeaders.AUTHORIZATION)).containsOnly("Bearer " + this.accessToken.getTokenValue());
115117
}
118+
119+
private Context context(Authentication authentication) {
120+
Map<Class<?>, Object> contextAttributes = new HashMap<>();
121+
contextAttributes.put(Authentication.class, authentication);
122+
return Context.of(SECURITY_REACTOR_CONTEXT_ATTRIBUTES_KEY, contextAttributes);
123+
}
116124
}

0 commit comments

Comments
 (0)