Skip to content

Commit 0a70e33

Browse files
committed
Merge branch '2.1.x'
Closes gh-18345
2 parents a354657 + 342a053 commit 0a70e33

File tree

5 files changed

+179
-48
lines changed

5 files changed

+179
-48
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/reactive/ReactiveOAuth2ClientAutoConfiguration.java

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,21 @@
1515
*/
1616
package org.springframework.boot.autoconfigure.security.oauth2.client.reactive;
1717

18-
import java.util.ArrayList;
19-
import java.util.List;
20-
2118
import reactor.core.publisher.Flux;
2219

2320
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
2421
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
25-
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2622
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
27-
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2823
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
2924
import org.springframework.boot.autoconfigure.condition.NoneNestedConditions;
30-
import org.springframework.boot.autoconfigure.security.oauth2.client.ClientsConfiguredCondition;
3125
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
32-
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesRegistrationAdapter;
3326
import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration;
3427
import org.springframework.boot.context.properties.EnableConfigurationProperties;
35-
import org.springframework.context.annotation.Bean;
3628
import org.springframework.context.annotation.Conditional;
3729
import org.springframework.context.annotation.Configuration;
30+
import org.springframework.context.annotation.Import;
3831
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
39-
import org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService;
40-
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
4132
import org.springframework.security.oauth2.client.registration.ClientRegistration;
42-
import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
43-
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
44-
import org.springframework.security.oauth2.client.web.server.AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository;
45-
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
4633

4734
/**
4835
* {@link EnableAutoConfiguration Auto-configuration} for Spring Security's Reactive
@@ -56,34 +43,10 @@
5643
@EnableConfigurationProperties(OAuth2ClientProperties.class)
5744
@Conditional(ReactiveOAuth2ClientAutoConfiguration.NonServletApplicationCondition.class)
5845
@ConditionalOnClass({ Flux.class, EnableWebFluxSecurity.class, ClientRegistration.class })
46+
@Import({ ReactiveOAuth2ClientConfigurations.ReactiveClientRegistrationRepositoryConfiguration.class,
47+
ReactiveOAuth2ClientConfigurations.ReactiveOAuth2ClientConfiguration.class })
5948
public class ReactiveOAuth2ClientAutoConfiguration {
6049

61-
@Bean
62-
@Conditional(ClientsConfiguredCondition.class)
63-
@ConditionalOnMissingBean(ReactiveClientRegistrationRepository.class)
64-
public InMemoryReactiveClientRegistrationRepository clientRegistrationRepository(
65-
OAuth2ClientProperties properties) {
66-
List<ClientRegistration> registrations = new ArrayList<>(
67-
OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(properties).values());
68-
return new InMemoryReactiveClientRegistrationRepository(registrations);
69-
}
70-
71-
@Bean
72-
@ConditionalOnBean(ReactiveClientRegistrationRepository.class)
73-
@ConditionalOnMissingBean
74-
public ReactiveOAuth2AuthorizedClientService authorizedClientService(
75-
ReactiveClientRegistrationRepository clientRegistrationRepository) {
76-
return new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);
77-
}
78-
79-
@Bean
80-
@ConditionalOnBean(ReactiveOAuth2AuthorizedClientService.class)
81-
@ConditionalOnMissingBean
82-
public ServerOAuth2AuthorizedClientRepository authorizedClientRepository(
83-
ReactiveOAuth2AuthorizedClientService authorizedClientService) {
84-
return new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService);
85-
}
86-
8750
static class NonServletApplicationCondition extends NoneNestedConditions {
8851

8952
NonServletApplicationCondition() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright 2012-2019 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.boot.autoconfigure.security.oauth2.client.reactive;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
23+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
24+
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
25+
import org.springframework.boot.autoconfigure.security.oauth2.client.ClientsConfiguredCondition;
26+
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
27+
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesRegistrationAdapter;
28+
import org.springframework.context.annotation.Bean;
29+
import org.springframework.context.annotation.Conditional;
30+
import org.springframework.context.annotation.Configuration;
31+
import org.springframework.security.config.web.server.ServerHttpSecurity;
32+
import org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService;
33+
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
34+
import org.springframework.security.oauth2.client.registration.ClientRegistration;
35+
import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository;
36+
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
37+
import org.springframework.security.oauth2.client.web.server.AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository;
38+
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
39+
import org.springframework.security.web.server.SecurityWebFilterChain;
40+
41+
/**
42+
* Reactive OAuth2 Client configurations.
43+
*
44+
* @author Madhura Bhave
45+
*/
46+
class ReactiveOAuth2ClientConfigurations {
47+
48+
@Configuration
49+
@Conditional(ClientsConfiguredCondition.class)
50+
@ConditionalOnMissingBean(ReactiveClientRegistrationRepository.class)
51+
static class ReactiveClientRegistrationRepositoryConfiguration {
52+
53+
@Bean
54+
InMemoryReactiveClientRegistrationRepository clientRegistrationRepository(OAuth2ClientProperties properties) {
55+
List<ClientRegistration> registrations = new ArrayList<>(
56+
OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(properties).values());
57+
return new InMemoryReactiveClientRegistrationRepository(registrations);
58+
}
59+
60+
}
61+
62+
@Configuration
63+
@ConditionalOnBean(ReactiveClientRegistrationRepository.class)
64+
static class ReactiveOAuth2ClientConfiguration {
65+
66+
@Bean
67+
@ConditionalOnMissingBean
68+
ReactiveOAuth2AuthorizedClientService authorizedClientService(
69+
ReactiveClientRegistrationRepository clientRegistrationRepository) {
70+
return new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);
71+
}
72+
73+
@Bean
74+
@ConditionalOnMissingBean
75+
ServerOAuth2AuthorizedClientRepository authorizedClientRepository(
76+
ReactiveOAuth2AuthorizedClientService authorizedClientService) {
77+
return new AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository(authorizedClientService);
78+
}
79+
80+
@Configuration
81+
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
82+
static class SecurityWebFilterChainConfiguration {
83+
84+
@Bean
85+
@ConditionalOnMissingBean
86+
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
87+
http.authorizeExchange().anyExchange().authenticated();
88+
http.oauth2Login();
89+
return http.build();
90+
}
91+
92+
}
93+
94+
}
95+
96+
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/reactive/ReactiveOAuth2ClientAutoConfigurationTests.java

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,27 @@
1818
import java.time.Duration;
1919
import java.util.ArrayList;
2020
import java.util.List;
21+
import java.util.stream.Collectors;
2122

2223
import org.junit.jupiter.api.Test;
2324
import reactor.core.publisher.Flux;
2425

26+
import org.springframework.beans.BeansException;
2527
import org.springframework.boot.autoconfigure.AutoConfigurations;
28+
import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration;
2629
import org.springframework.boot.test.context.FilteredClassLoader;
30+
import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext;
2731
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
32+
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
2833
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
34+
import org.springframework.context.ApplicationContext;
35+
import org.springframework.context.ApplicationContextAware;
2936
import org.springframework.context.annotation.Bean;
3037
import org.springframework.context.annotation.Configuration;
3138
import org.springframework.context.annotation.Import;
39+
import org.springframework.security.config.BeanIds;
3240
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
41+
import org.springframework.security.config.web.server.ServerHttpSecurity;
3342
import org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService;
3443
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
3544
import org.springframework.security.oauth2.client.registration.ClientRegistration;
@@ -38,7 +47,11 @@
3847
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
3948
import org.springframework.security.oauth2.client.web.server.AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository;
4049
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository;
50+
import org.springframework.security.oauth2.client.web.server.authentication.OAuth2LoginAuthenticationWebFilter;
4151
import org.springframework.security.oauth2.core.AuthorizationGrantType;
52+
import org.springframework.security.web.server.SecurityWebFilterChain;
53+
import org.springframework.test.util.ReflectionTestUtils;
54+
import org.springframework.web.server.WebFilter;
4255

4356
import static org.assertj.core.api.Assertions.assertThat;
4457

@@ -49,8 +62,8 @@
4962
*/
5063
class ReactiveOAuth2ClientAutoConfigurationTests {
5164

52-
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
53-
.withConfiguration(AutoConfigurations.of(ReactiveOAuth2ClientAutoConfiguration.class));
65+
private ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration(AutoConfigurations
66+
.of(ReactiveOAuth2ClientAutoConfiguration.class, ReactiveSecurityAutoConfiguration.class));
5467

5568
private static final String REGISTRATION_PREFIX = "spring.security.oauth2.client.registration";
5669

@@ -82,15 +95,19 @@ void clientRegistrationRepositoryBeanShouldBeCreatedWhenPropertiesPresent() {
8295
}
8396

8497
@Test
85-
void authorizedClientServiceBeanIsConditionalOnClientRegistrationRepository() {
86-
this.contextRunner
87-
.run((context) -> assertThat(context).doesNotHaveBean(ReactiveOAuth2AuthorizedClientService.class));
98+
void authorizedClientServiceAndRepositoryBeansAreConditionalOnClientRegistrationRepository() {
99+
this.contextRunner.run((context) -> {
100+
assertThat(context).doesNotHaveBean(ReactiveOAuth2AuthorizedClientService.class);
101+
assertThat(context).doesNotHaveBean(ServerOAuth2AuthorizedClientRepository.class);
102+
});
88103
}
89104

90105
@Test
91-
void configurationRegistersAuthorizedClientServiceBean() {
92-
this.contextRunner.withUserConfiguration(ReactiveClientRepositoryConfiguration.class).run(
93-
(context) -> assertThat(context).hasSingleBean(InMemoryReactiveClientRegistrationRepository.class));
106+
void configurationRegistersAuthorizedClientServiceAndRepositoryBeans() {
107+
this.contextRunner.withUserConfiguration(ReactiveClientRepositoryConfiguration.class).run((context) -> {
108+
assertThat(context).hasSingleBean(InMemoryReactiveOAuth2AuthorizedClientService.class);
109+
assertThat(context).hasSingleBean(AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository.class);
110+
});
94111
}
95112

96113
@Test
@@ -124,6 +141,22 @@ void authorizedClientRepositoryBeanIsConditionalOnMissingBean() {
124141
});
125142
}
126143

144+
@Test
145+
void securityWebFilterChainBeanConditionalOnWebApplication() {
146+
this.contextRunner.withUserConfiguration(ReactiveOAuth2AuthorizedClientRepositoryConfiguration.class)
147+
.run((context) -> assertThat(context).doesNotHaveBean(SecurityWebFilterChain.class));
148+
}
149+
150+
@Test
151+
void configurationRegistersSecurityWebFilterChainBean() { // gh-17949
152+
new ReactiveWebApplicationContextRunner()
153+
.withConfiguration(AutoConfigurations.of(ReactiveOAuth2ClientAutoConfiguration.class))
154+
.withUserConfiguration(ReactiveOAuth2AuthorizedClientServiceConfiguration.class,
155+
ServerHttpSecurityConfiguration.class)
156+
.run((context) -> assertThat(getFilters(context, OAuth2LoginAuthenticationWebFilter.class))
157+
.isNotNull());
158+
}
159+
127160
@Test
128161
void autoConfigurationConditionalOnClassFlux() {
129162
assertWhenClassNotPresent(Flux.class);
@@ -147,6 +180,15 @@ private void assertWhenClassNotPresent(Class<?> classToFilter) {
147180
.run((context) -> assertThat(context).doesNotHaveBean(ReactiveOAuth2ClientAutoConfiguration.class));
148181
}
149182

183+
@SuppressWarnings("unchecked")
184+
private List<WebFilter> getFilters(AssertableReactiveWebApplicationContext context,
185+
Class<? extends WebFilter> filter) {
186+
SecurityWebFilterChain filterChain = (SecurityWebFilterChain) context
187+
.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN);
188+
List<WebFilter> filters = (List<WebFilter>) ReflectionTestUtils.getField(filterChain, "filters");
189+
return filters.stream().filter(filter::isInstance).collect(Collectors.toList());
190+
}
191+
150192
@Configuration(proxyBeanMethods = false)
151193
static class ReactiveClientRepositoryConfiguration {
152194

@@ -196,4 +238,24 @@ ServerOAuth2AuthorizedClientRepository testAuthorizedClientRepository(
196238

197239
}
198240

241+
@Configuration(proxyBeanMethods = false)
242+
static class ServerHttpSecurityConfiguration {
243+
244+
@Bean
245+
ServerHttpSecurity http() {
246+
TestServerHttpSecurity httpSecurity = new TestServerHttpSecurity();
247+
return httpSecurity;
248+
}
249+
250+
static class TestServerHttpSecurity extends ServerHttpSecurity implements ApplicationContextAware {
251+
252+
@Override
253+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
254+
super.setApplicationContext(applicationContext);
255+
}
256+
257+
}
258+
259+
}
260+
199261
}

spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-reactive-oauth2-client/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
</properties>
1616
<dependencies>
1717
<!-- Compile -->
18+
<dependency>
19+
<groupId>org.springframework.boot</groupId>
20+
<artifactId>spring-boot-starter-actuator</artifactId>
21+
</dependency>
1822
<dependency>
1923
<groupId>org.springframework.boot</groupId>
2024
<artifactId>spring-boot-starter-oauth2-client</artifactId>

spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-reactive-oauth2-client/src/test/java/smoketest/oauth2/client/SampleReactiveOAuth2ClientApplicationTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,10 @@ void loginShouldHaveBothOAuthClientsToChooseFrom() {
4848
assertThat(bodyString).contains("/oauth2/authorization/github-client-2");
4949
}
5050

51+
@Test
52+
public void actuatorShouldBeSecuredByOAuth() {
53+
this.webTestClient.get().uri("/actuator/health").exchange().expectStatus().isFound().expectHeader()
54+
.valueEquals("Location", "/login");
55+
}
56+
5157
}

0 commit comments

Comments
 (0)