mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
Add auto-config for reactive OAuth2 Resource Server
Closes gh-13948
This commit is contained in:
parent
319fec4be4
commit
bc6e4e6e55
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.security.oauth2.resource.reactive;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
|
||||
import org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Reactive OAuth2 resource server
|
||||
* support.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 2.1.0
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureBefore(ReactiveSecurityAutoConfiguration.class)
|
||||
@EnableConfigurationProperties(OAuth2ResourceServerProperties.class)
|
||||
@ConditionalOnClass({ EnableWebFluxSecurity.class, BearerTokenAuthenticationToken.class })
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
@Import({ ReactiveOAuth2ResourceServerJwkConfiguration.class,
|
||||
ReactiveOAuth2ResourceServerWebSecurityConfiguration.class })
|
||||
public class ReactiveOAuth2ResourceServerAutoConfiguration {
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.security.oauth2.resource.reactive;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||
|
||||
/**
|
||||
* Configures a {@link ReactiveJwtDecoder} when a JWK Set URI is available.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@Configuration
|
||||
class ReactiveOAuth2ResourceServerJwkConfiguration {
|
||||
|
||||
private final OAuth2ResourceServerProperties properties;
|
||||
|
||||
ReactiveOAuth2ResourceServerJwkConfiguration(
|
||||
OAuth2ResourceServerProperties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.security.oauth2.resource.jwt.jwk.set-uri")
|
||||
@ConditionalOnMissingBean
|
||||
public ReactiveJwtDecoder jwtDecoder() {
|
||||
return new NimbusReactiveJwtDecoder(
|
||||
this.properties.getJwt().getJwk().getSetUri());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.security.oauth2.resource.reactive;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
|
||||
/**
|
||||
* Configures a {@link SecurityWebFilterChain} for Reactive OAuth2 resource server support
|
||||
* if a {@link ReactiveJwtDecoder} bean is present.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnBean(ReactiveJwtDecoder.class)
|
||||
class ReactiveOAuth2ResourceServerWebSecurityConfiguration {
|
||||
|
||||
private final ReactiveJwtDecoder jwtDecoder;
|
||||
|
||||
ReactiveOAuth2ResourceServerWebSecurityConfiguration(ReactiveJwtDecoder jwtDecoder) {
|
||||
this.jwtDecoder = jwtDecoder;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
|
||||
http.authorizeExchange().anyExchange().authenticated().and().oauth2()
|
||||
.resourceServer().jwt().jwtDecoder(this.jwtDecoder);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for Spring Security's Reactive OAuth2 resource server.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.security.oauth2.resource.reactive;
|
@ -106,6 +106,7 @@ org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.task.TaskExecutorAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
|
||||
|
@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.security.oauth2.resource.reactive;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.FilteredClassLoader;
|
||||
import org.springframework.boot.test.context.assertj.AssertableReactiveWebApplicationContext;
|
||||
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||
import org.springframework.security.config.BeanIds;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||
import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtReactiveAuthenticationManager;
|
||||
import org.springframework.security.web.server.MatcherSecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link ReactiveOAuth2ResourceServerAutoConfiguration}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
*/
|
||||
public class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
||||
|
||||
private ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations
|
||||
.of(ReactiveOAuth2ResourceServerAutoConfiguration.class))
|
||||
.withUserConfiguration(TestConfig.class);
|
||||
|
||||
@Test
|
||||
public void autoConfigurationShouldConfigureResourceServer() {
|
||||
this.contextRunner.withPropertyValues(
|
||||
"spring.security.oauth2.resource.jwt.jwk.set-uri=http://jwk-set-uri.com")
|
||||
.run((context) -> {
|
||||
assertThat(context.getBean(ReactiveJwtDecoder.class))
|
||||
.isInstanceOf(NimbusReactiveJwtDecoder.class);
|
||||
assertFilterConfiguredWithJwtAuthenticationManager(context);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoConfigurationWhenJwkSetUriNullShouldNotFail() {
|
||||
this.contextRunner.run((context) -> assertThat(context)
|
||||
.doesNotHaveBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jwtDecoderBeanIsConditionalOnMissingBean() {
|
||||
this.contextRunner.withPropertyValues(
|
||||
"spring.security.oauth2.resource.jwt.jwk.set-uri=http://jwk-set-uri.com")
|
||||
.withUserConfiguration(JwtDecoderConfig.class)
|
||||
.run((this::assertFilterConfiguredWithJwtAuthenticationManager));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoConfigurationShouldBeConditionalOnBearerTokenAuthenticationTokenClass() {
|
||||
this.contextRunner.withPropertyValues(
|
||||
"spring.security.oauth2.resource.jwt.jwk.set-uri=http://jwk-set-uri.com")
|
||||
.withUserConfiguration(JwtDecoderConfig.class)
|
||||
.withClassLoader(
|
||||
new FilteredClassLoader(BearerTokenAuthenticationToken.class))
|
||||
.run((context) -> assertThat(context)
|
||||
.doesNotHaveBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void autoConfigurationWhenSecurityWebFilterChainConfigPresentShouldNotAddOne() {
|
||||
this.contextRunner.withPropertyValues(
|
||||
"spring.security.oauth2.resource.jwt.jwk.set-uri=http://jwk-set-uri.com")
|
||||
.withUserConfiguration(SecurityWebFilterChainConfig.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(SecurityWebFilterChain.class);
|
||||
assertThat(context).hasBean("testSpringSecurityFilterChain");
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void assertFilterConfiguredWithJwtAuthenticationManager(
|
||||
AssertableReactiveWebApplicationContext context) {
|
||||
MatcherSecurityWebFilterChain filterChain = (MatcherSecurityWebFilterChain) context
|
||||
.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN);
|
||||
List<WebFilter> filters = (List<WebFilter>) ReflectionTestUtils
|
||||
.getField(filterChain, "filters");
|
||||
AuthenticationWebFilter webFilter = (AuthenticationWebFilter) filters.stream()
|
||||
.filter((f) -> f instanceof AuthenticationWebFilter).findFirst()
|
||||
.orElse(null);
|
||||
ReactiveAuthenticationManager authenticationManager = (ReactiveAuthenticationManager) ReflectionTestUtils
|
||||
.getField(webFilter, "authenticationManager");
|
||||
assertThat(authenticationManager)
|
||||
.isInstanceOf(JwtReactiveAuthenticationManager.class);
|
||||
|
||||
}
|
||||
|
||||
@EnableWebFluxSecurity
|
||||
static class TestConfig {
|
||||
|
||||
@Bean
|
||||
public MapReactiveUserDetailsService userDetailsService() {
|
||||
return mock(MapReactiveUserDetailsService.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class JwtDecoderConfig {
|
||||
|
||||
@Bean
|
||||
public ReactiveJwtDecoder decoder() {
|
||||
return mock(ReactiveJwtDecoder.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SecurityWebFilterChainConfig {
|
||||
|
||||
@Bean
|
||||
SecurityWebFilterChain testSpringSecurityFilterChain(ServerHttpSecurity http,
|
||||
ReactiveJwtDecoder decoder) {
|
||||
http.authorizeExchange().pathMatchers("/message/**").hasRole("ADMIN")
|
||||
.anyExchange().authenticated().and().oauth2().resourceServer().jwt()
|
||||
.jwtDecoder(decoder);
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -165,7 +165,7 @@
|
||||
<spring-plugin.version>1.2.0.RELEASE</spring-plugin.version>
|
||||
<spring-restdocs.version>2.0.2.RELEASE</spring-restdocs.version>
|
||||
<spring-retry.version>1.2.2.RELEASE</spring-retry.version>
|
||||
<spring-security.version>5.1.0.M2</spring-security.version>
|
||||
<spring-security.version>5.1.0.BUILD-SNAPSHOT</spring-security.version>
|
||||
<spring-session-bom.version>Bean-M1</spring-session-bom.version>
|
||||
<spring-ws.version>3.0.3.RELEASE</spring-ws.version>
|
||||
<sqlite-jdbc.version>3.23.1</sqlite-jdbc.version>
|
||||
|
@ -3286,7 +3286,10 @@ following example:
|
||||
spring.security.oauth2.resource.jwt.jwk.set-uri=https://example.com/oauth2/default/v1/keys
|
||||
----
|
||||
|
||||
Alternatively, you can define your own `JwtDecoder` bean.
|
||||
The same properties are applicable for both servlet and reactive applications.
|
||||
|
||||
Alternatively, you can define your own `JwtDecoder` bean for servlet applications
|
||||
or a `ReactiveJwtDecoder` for reactive applications.
|
||||
|
||||
|
||||
|
||||
|
@ -137,6 +137,9 @@ The following sample applications are provided:
|
||||
| link:spring-boot-sample-oauth2-resource-server[spring-boot-sample-oauth2-resource-server]
|
||||
| Configure an OAuth2 resource server
|
||||
|
||||
| link:spring-boot-sample-reactive-oauth2-resource-server[spring-boot-sample-reactive-oauth2-resource-server]
|
||||
| Configure a Reactive OAuth2 resource server
|
||||
|
||||
| link:spring-boot-sample-parent-context[spring-boot-sample-parent-context]
|
||||
| Application that uses an `ApplicationContext` with a parent
|
||||
|
||||
|
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<!-- Your own application should inherit from spring-boot-starter-parent -->
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-samples</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<artifactId>spring-boot-sample-reactive-oauth2-resource-server</artifactId>
|
||||
<name>Spring Boot Sample Reactive OAuth2 Resource Server</name>
|
||||
<description>Spring Boot Sample Reactive Resource Server</description>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<!-- Compile -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-oauth2-jose</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-oauth2-resource-server</artifactId>
|
||||
</dependency>
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>mockwebserver</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample.oauth2.resource;
|
||||
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
public class ExampleController {
|
||||
|
||||
@GetMapping("/")
|
||||
public String index(@AuthenticationPrincipal Jwt jwt) {
|
||||
return String.format("Hello, %s!", jwt.getSubject());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample.oauth2.resource;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SampleReactiveOAuth2ResourceServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleReactiveOAuth2ResourceServerApplication.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
spring:
|
||||
security:
|
||||
oauth2:
|
||||
resource:
|
||||
jwt:
|
||||
jwk:
|
||||
# To run the application, replace this with a valid JWK Set URI
|
||||
set-uri: https://example.com/oauth2/default/v1/keys
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sample.oauth2.resource;
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class SampleReactiveOAuth2ResourceServerApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private WebTestClient webTestClient;
|
||||
|
||||
private static MockWebServer server = new MockWebServer();
|
||||
|
||||
private static final String VALID_TOKEN = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJzdWJqZWN0Iiwic2NvcGUiOiJtZXNzYWdlOnJlYWQi"
|
||||
+ "LCJleHAiOjQ2ODM4MDUxNDF9.h-j6FKRFdnTdmAueTZCdep45e6DPwqM68ZQ8doIJ1exi9YxAlbWzOwId6Bd0L5YmCmp63gGQgsBUBLzwnZQ8kLUgU"
|
||||
+ "OBEC3UzSWGRqMskCY9_k9pX0iomX6IfF3N0PaYs0WPC4hO1s8wfZQ-6hKQ4KigFi13G9LMLdH58PRMK0pKEvs3gCbHJuEPw-K5ORlpdnleUTQIwIN"
|
||||
+ "afU57cmK3KocTeknPAM_L716sCuSYGvDl6xUTXO7oPdrXhS_EhxLP6KxrpI1uD4Ea_5OWTh7S0Wx5LLDfU6wBG1DowN20d374zepOIEkR-Jnmr_Ql"
|
||||
+ "R44vmRqS5ncrF-1R0EGcPX49U6A";
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() throws Exception {
|
||||
server.start();
|
||||
String url = server.url("/.well-known/jwks.json").toString();
|
||||
server.enqueue(mockResponse());
|
||||
System.setProperty("spring.security.oauth2.resource.jwt.jwk.set-uri", url);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void shutdown() throws Exception {
|
||||
server.shutdown();
|
||||
System.clearProperty("spring.security.oauth2.resource.jwt.jwk.set-uri");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenValidTokenShouldBeOk() {
|
||||
this.webTestClient.get().uri("/")
|
||||
.headers(headers -> headers.setBearerAuth(VALID_TOKEN)).exchange()
|
||||
.expectStatus().isOk().expectBody(String.class)
|
||||
.isEqualTo("Hello, subject!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenNoTokenShouldBeUnauthorized() {
|
||||
this.webTestClient.get().uri("/").exchange().expectStatus().isUnauthorized()
|
||||
.expectHeader().valueEquals(HttpHeaders.WWW_AUTHENTICATE, "Bearer");
|
||||
}
|
||||
|
||||
private static MockResponse mockResponse() {
|
||||
String body = "{\"keys\":[{\"p\":\"2p-ViY7DE9ZrdWQb544m0Jp7Cv03YCSljqfim9pD4ALhObX0OrAznOiowTjwBky9JGffMw"
|
||||
+ "DBVSfJSD9TSU7aH2sbbfi0bZLMdekKAuimudXwUqPDxrrg0BCyvCYgLmKjbVT3zcdylWSog93CNTxGDPzauu-oc0XPNKCXnaDpNvE\""
|
||||
+ ",\"kty\":\"RSA\",\"q\":\"sP_QYavrpBvSJ86uoKVGj2AGl78CSsAtpf1ybSY5TwUlorXSdqapRbY69Y271b0aMLzlleUn9ZTBO"
|
||||
+ "1dlKV2_dw_lPADHVia8z3pxL-8sUhIXLsgj4acchMk4c9YX-sFh07xENnyZ-_TXm3llPLuL67HUfBC2eKe800TmCYVWc9U\",\"d\""
|
||||
+ ":\"bn1nFxCQT4KLTHqo8mo9HvHD0cRNRNdWcKNnnEQkCF6tKbt-ILRyQGP8O40axLd7CoNVG9c9p_-g4-2kwCtLJNv_STLtwfpCY7"
|
||||
+ "VN5o6-ZIpfTjiW6duoPrLWq64Hm_4LOBQTiZfUPcLhsuJRHbWqakj-kV_YbUyC2Ocf_dd8IAQcSrAU2SCcDebhDCWwRUFvaa9V5eq0"
|
||||
+ "851S9goaA-AJz-JXyePH6ZFr8JxmWkWxYZ5kdcMD-sm9ZbxE0CaEk32l4fE4hR-L8x2dDtjWA-ahKCZ091z-gV3HWtR2JOjvxoNRjxUo"
|
||||
+ "3UxaGiFJHWNIl0EYUJZu1Cb-5wIlEI7wPx5mwQ\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"qi\":\"qS0OK4"
|
||||
+ "8M2CIAA6_4Wdw4EbCaAfcTLf5Oy9t5BOF_PFUKqoSpZ6JsT5H0a_4zkjt-oI969v78OTlvBKbmEyKO-KeytzHBAA5CsLmVcz0THrMSg6o"
|
||||
+ "XZqu66MPnvWoZN9FEN5TklPOvBFm8Bg1QZ3k-YMVaM--DLvhaYR95_mqaz50\",\"dp\":\"Too2NozLGD1XrXyhabZvy1E0EuaVFj0UHQ"
|
||||
+ "PDLSpkZ_2g3BK6Art6T0xmE8RYtmqrKIEIdlI3IliAvyvAx_1D7zWTTRaj-xlZyqJFrnXWL7zj8UxT8PkB-r2E-ILZ3NAi1gxIWezlBTZ8"
|
||||
+ "M6NfObDFmbTc_3tJkN_raISo8z_ziIE\",\"dq\":\"U0yhSkY5yOsa9YcMoigGVBWSJLpNHtbg5NypjHrPv8OhWbkOSq7WvSstBkF"
|
||||
+ "k5AtyFvvfZLMLIkWWxxGzV0t6f1MoxBtttLrYYyCxwihiiGFhLbAdSuZ1wnxcqA9bC7UVECvrQmVTpsMs8UupfHKbQBpZ8OWAqrn"
|
||||
+ "uYNNtG4_4Bt0\",\"n\":\"lygtuZj0lJjqOqIWocF8Bb583QDdq-aaFg8PesOp2-EDda6GqCpL-_NZVOflNGX7XIgjsWHcPsQHs"
|
||||
+ "V9gWuOzSJ0iEuWvtQ6eGBP5M6m7pccLNZfwUse8Cb4Ngx3XiTlyuqM7pv0LPyppZusfEHVEdeelou7Dy9k0OQ_nJTI3b2E1WBoHC5"
|
||||
+ "8CJ453lo4gcBm1efURN3LIVc1V9NQY_ESBKVdwqYyoJPEanURLVGRd6cQKn6YrCbbIRHjqAyqOE-z3KmgDJnPriljfR5XhSGyM9eq"
|
||||
+ "D9Xpy6zu_MAeMJJfSArp857zLPk-Wf5VP9STAcjyfdBIybMKnwBYr2qHMT675hQ\"}]}";
|
||||
return new MockResponse()
|
||||
.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
||||
.setResponseCode(200).setBody(body);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user