Skip to content

Commit f796eda

Browse files
committed
Merge pull request #49029 from itsmevichu
* gh-49029: Polish "Add customizers for opaque token introspector builders" Add customizers for opaque token introspector builders Closes gh-49029
2 parents 050d068 + 632a19a commit f796eda

7 files changed

Lines changed: 155 additions & 11 deletions

File tree

documentation/spring-boot-docs/src/docs/antora/modules/reference/pages/web/spring-security.adoc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,11 @@ spring:
299299
----
300300

301301
Again, the same properties are applicable for both servlet and reactive applications.
302-
Alternatively, you can define your own javadoc:org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector[] bean for servlet applications or a javadoc:org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector[] for reactive applications.
302+
303+
The result is an auto-configured introspector. Either a javadoc:org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector[] or, in a reactive application, a javadoc:org.springframework.security.oauth2.server.resource.introspection.SpringReactiveOpaqueTokenIntrospector[].
304+
These auto-configured introspectors can be customized using javadoc:org.springframework.boot.security.oauth2.server.resource.autoconfigure.SpringOpaqueTokenIntrospectorBuilderCustomizer[] and javadoc:org.springframework.boot.security.oauth2.server.resource.autoconfigure.reactive.SpringReactiveOpaqueTokenIntrospectorBuilderCustomizer[] beans respectively.
305+
306+
To take complete control of the introspection, define your own javadoc:org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector[] or javadoc:org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector[] bean.
303307

304308

305309

module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/OpaqueTokenIntrospectionConfiguration.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.security.oauth2.server.resource.autoconfigure;
1818

19+
import org.springframework.beans.factory.ObjectProvider;
1920
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2021
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2122
import org.springframework.context.annotation.Bean;
@@ -36,16 +37,19 @@ class OpaqueTokenIntrospectionConfiguration {
3637

3738
@Bean
3839
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.opaquetoken.introspection-uri")
39-
SpringOpaqueTokenIntrospector opaqueTokenIntrospector(OAuth2ResourceServerProperties properties) {
40+
SpringOpaqueTokenIntrospector opaqueTokenIntrospector(OAuth2ResourceServerProperties properties,
41+
ObjectProvider<SpringOpaqueTokenIntrospectorBuilderCustomizer> customizers) {
4042
OAuth2ResourceServerProperties.Opaquetoken token = properties.getOpaquetoken();
4143
Assert.state(token.getClientId() != null,
4244
"No 'spring.security.oauth2.resourceserver.opaquetoken.client-id' property specified");
4345
Assert.state(token.getClientSecret() != null,
4446
"No 'spring.security.oauth2.resourceserver.opaquetoken.client-secret' property specified");
45-
return SpringOpaqueTokenIntrospector.withIntrospectionUri(token.getIntrospectionUri())
47+
SpringOpaqueTokenIntrospector.Builder builder = SpringOpaqueTokenIntrospector
48+
.withIntrospectionUri(token.getIntrospectionUri())
4649
.clientId(token.getClientId())
47-
.clientSecret(token.getClientSecret())
48-
.build();
50+
.clientSecret(token.getClientSecret());
51+
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
52+
return builder.build();
4953
}
5054

5155
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2012-present 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.security.oauth2.server.resource.autoconfigure;
18+
19+
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
20+
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector.Builder;
21+
22+
/**
23+
* Callback interface for the customization of the {@link Builder
24+
* SpringOpaqueTokenIntrospector.Builder} used to create the auto-configured
25+
* {@link SpringOpaqueTokenIntrospector}.
26+
*
27+
* @author Vishnutheep B
28+
* @since 4.1.0
29+
*/
30+
@FunctionalInterface
31+
public interface SpringOpaqueTokenIntrospectorBuilderCustomizer {
32+
33+
/**
34+
* Customize the given {@code builder}.
35+
* @param builder the {@code builder} to customize
36+
*/
37+
void customize(SpringOpaqueTokenIntrospector.Builder builder);
38+
39+
}

module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOpaqueTokenIntrospectionClientConfiguration.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.boot.security.oauth2.server.resource.autoconfigure.reactive;
1818

19+
import org.springframework.beans.factory.ObjectProvider;
1920
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2021
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2122
import org.springframework.boot.security.oauth2.server.resource.autoconfigure.OAuth2ResourceServerProperties;
@@ -37,16 +38,19 @@ class ReactiveOpaqueTokenIntrospectionClientConfiguration {
3738

3839
@Bean
3940
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.opaquetoken.introspection-uri")
40-
SpringReactiveOpaqueTokenIntrospector reactiveOpaqueTokenIntrospector(OAuth2ResourceServerProperties properties) {
41+
SpringReactiveOpaqueTokenIntrospector reactiveOpaqueTokenIntrospector(OAuth2ResourceServerProperties properties,
42+
ObjectProvider<SpringReactiveOpaqueTokenIntrospectorBuilderCustomizer> customizers) {
4143
OAuth2ResourceServerProperties.Opaquetoken token = properties.getOpaquetoken();
4244
Assert.state(token.getClientId() != null,
4345
"No 'spring.security.oauth2.resourceserver.opaquetoken.client-id' property specified");
4446
Assert.state(token.getClientSecret() != null,
4547
"No 'spring.security.oauth2.resourceserver.opaquetoken.client-secret' property specified");
46-
return SpringReactiveOpaqueTokenIntrospector.withIntrospectionUri(token.getIntrospectionUri())
48+
SpringReactiveOpaqueTokenIntrospector.Builder builder = SpringReactiveOpaqueTokenIntrospector
49+
.withIntrospectionUri(token.getIntrospectionUri())
4750
.clientId(token.getClientId())
48-
.clientSecret(token.getClientSecret())
49-
.build();
51+
.clientSecret(token.getClientSecret());
52+
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
53+
return builder.build();
5054
}
5155

5256
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2012-present 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.security.oauth2.server.resource.autoconfigure.reactive;
18+
19+
import org.springframework.security.oauth2.server.resource.introspection.SpringReactiveOpaqueTokenIntrospector;
20+
import org.springframework.security.oauth2.server.resource.introspection.SpringReactiveOpaqueTokenIntrospector.Builder;
21+
22+
/**
23+
* Callback interface for the customization of the {@link Builder
24+
* SpringReactiveOpaqueTokenIntrospector.Builder} used to create the auto-configured
25+
* {@link SpringReactiveOpaqueTokenIntrospector}.
26+
*
27+
* @author Vishnutheep B
28+
* @since 4.1.0
29+
*/
30+
@FunctionalInterface
31+
public interface SpringReactiveOpaqueTokenIntrospectorBuilderCustomizer {
32+
33+
/**
34+
* Customize the given {@code builder}.
35+
* @param builder the {@code builder} to customize
36+
*/
37+
void customize(SpringReactiveOpaqueTokenIntrospector.Builder builder);
38+
39+
}

module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/OAuth2ResourceServerAutoConfigurationTests.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,21 @@ void autoConfigurationWhenIntrospectionUriAvailableShouldConfigureIntrospectionC
428428
"spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com",
429429
"spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id",
430430
"spring.security.oauth2.resourceserver.opaquetoken.client-secret=my-client-secret")
431-
.run((context) -> assertThat(context).hasSingleBean(OpaqueTokenIntrospector.class));
431+
.run((context) -> {
432+
assertThat(context).hasSingleBean(OpaqueTokenIntrospector.class);
433+
assertSpringOpaqueTokenIntrospectorBuilderCustomization(context);
434+
});
435+
}
436+
437+
private void assertSpringOpaqueTokenIntrospectorBuilderCustomization(AssertableWebApplicationContext context) {
438+
SpringOpaqueTokenIntrospectorBuilderCustomizer customizer = context
439+
.getBean("opaqueTokenIntrospectorBuilderCustomizer", SpringOpaqueTokenIntrospectorBuilderCustomizer.class);
440+
SpringOpaqueTokenIntrospectorBuilderCustomizer anotherCustomizer = context.getBean(
441+
"anotherOpaqueTokenIntrospectorBuilderCustomizer",
442+
SpringOpaqueTokenIntrospectorBuilderCustomizer.class);
443+
InOrder inOrder = inOrder(customizer, anotherCustomizer);
444+
inOrder.verify(customizer).customize(any());
445+
inOrder.verify(anotherCustomizer).customize(any());
432446
}
433447

434448
@Test
@@ -854,6 +868,18 @@ JwkSetUriJwtDecoderBuilderCustomizer anotherDecoderBuilderCustomizer() {
854868
return mock(JwkSetUriJwtDecoderBuilderCustomizer.class);
855869
}
856870

871+
@Bean
872+
@Order(1)
873+
SpringOpaqueTokenIntrospectorBuilderCustomizer opaqueTokenIntrospectorBuilderCustomizer() {
874+
return mock(SpringOpaqueTokenIntrospectorBuilderCustomizer.class);
875+
}
876+
877+
@Bean
878+
@Order(2)
879+
SpringOpaqueTokenIntrospectorBuilderCustomizer anotherOpaqueTokenIntrospectorBuilderCustomizer() {
880+
return mock(SpringOpaqueTokenIntrospectorBuilderCustomizer.class);
881+
}
882+
857883
}
858884

859885
@Configuration(proxyBeanMethods = false)

module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,23 @@ void autoConfigurationWhenIntrospectionUriAvailableShouldConfigureIntrospectionC
400400
"spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://check-token.com",
401401
"spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id",
402402
"spring.security.oauth2.resourceserver.opaquetoken.client-secret=my-client-secret")
403-
.run((context) -> assertThat(context).hasSingleBean(ReactiveOpaqueTokenIntrospector.class));
403+
.run((context) -> {
404+
assertThat(context).hasSingleBean(ReactiveOpaqueTokenIntrospector.class);
405+
assertSpringReactiveOpaqueTokenIntrospectorBuilderCustomization(context);
406+
});
407+
}
408+
409+
private void assertSpringReactiveOpaqueTokenIntrospectorBuilderCustomization(
410+
AssertableReactiveWebApplicationContext context) {
411+
SpringReactiveOpaqueTokenIntrospectorBuilderCustomizer customizer = context.getBean(
412+
"reactiveOpaqueTokenIntrospectorBuilderCustomizer",
413+
SpringReactiveOpaqueTokenIntrospectorBuilderCustomizer.class);
414+
SpringReactiveOpaqueTokenIntrospectorBuilderCustomizer anotherCustomizer = context.getBean(
415+
"anotherReactiveOpaqueTokenIntrospectorBuilderCustomizer",
416+
SpringReactiveOpaqueTokenIntrospectorBuilderCustomizer.class);
417+
InOrder inOrder = inOrder(customizer, anotherCustomizer);
418+
inOrder.verify(customizer).customize(any());
419+
inOrder.verify(anotherCustomizer).customize(any());
404420
}
405421

406422
@Test
@@ -865,6 +881,18 @@ JwkSetUriReactiveJwtDecoderBuilderCustomizer anotherDecoderBuilderCustomizer() {
865881
return mock(JwkSetUriReactiveJwtDecoderBuilderCustomizer.class);
866882
}
867883

884+
@Bean
885+
@Order(1)
886+
SpringReactiveOpaqueTokenIntrospectorBuilderCustomizer reactiveOpaqueTokenIntrospectorBuilderCustomizer() {
887+
return mock(SpringReactiveOpaqueTokenIntrospectorBuilderCustomizer.class);
888+
}
889+
890+
@Bean
891+
@Order(2)
892+
SpringReactiveOpaqueTokenIntrospectorBuilderCustomizer anotherReactiveOpaqueTokenIntrospectorBuilderCustomizer() {
893+
return mock(SpringReactiveOpaqueTokenIntrospectorBuilderCustomizer.class);
894+
}
895+
868896
}
869897

870898
@Configuration(proxyBeanMethods = false)

0 commit comments

Comments
 (0)