Add OAuth Support for HTTP Interface Client
Closes gh-16858
This commit is contained in:
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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 clients copy of the License at
|
||||
*
|
||||
* https://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.security.docs.features.integrations.rest.clientregistrationid;
|
||||
|
||||
/**
|
||||
* A user.
|
||||
* @param login
|
||||
* @param id
|
||||
* @param name
|
||||
* @author Rob Winch
|
||||
* @see UserService
|
||||
*/
|
||||
public record User(String login, int id, String name) {
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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 clients copy of the License at
|
||||
*
|
||||
* https://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.security.docs.features.integrations.rest.clientregistrationid;
|
||||
|
||||
import org.springframework.security.oauth2.client.annotation.ClientRegistrationId;
|
||||
import org.springframework.web.service.annotation.GetExchange;
|
||||
import org.springframework.web.service.annotation.HttpExchange;
|
||||
|
||||
/**
|
||||
* Demonstrates a service for {@link ClientRegistrationId} and HTTP Interface clients.
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@HttpExchange
|
||||
public interface UserService {
|
||||
|
||||
// tag::getAuthenticatedUser[]
|
||||
@GetExchange("/user")
|
||||
@ClientRegistrationId("github")
|
||||
User getAuthenticatedUser();
|
||||
// end::getAuthenticatedUser[]
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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 clients copy of the License at
|
||||
*
|
||||
* https://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.security.docs.features.integrations.rest.configurationrestclient;
|
||||
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.docs.features.integrations.rest.clientregistrationid.UserService;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.web.client.support.OAuth2RestClientHttpServiceGroupConfigurer;
|
||||
import org.springframework.web.client.support.RestClientHttpServiceGroupConfigurer;
|
||||
import org.springframework.web.service.registry.ImportHttpServices;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Documentation for {@link OAuth2RestClientHttpServiceGroupConfigurer}.
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ImportHttpServices(types = UserService.class)
|
||||
public class RestClientHttpInterfaceIntegrationConfiguration {
|
||||
|
||||
// tag::config[]
|
||||
@Bean
|
||||
OAuth2RestClientHttpServiceGroupConfigurer securityConfigurer(
|
||||
OAuth2AuthorizedClientManager manager) {
|
||||
return OAuth2RestClientHttpServiceGroupConfigurer.from(manager);
|
||||
}
|
||||
// end::config[]
|
||||
|
||||
@Bean
|
||||
OAuth2AuthorizedClientManager authorizedClientManager() {
|
||||
return mock(OAuth2AuthorizedClientManager.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
RestClientHttpServiceGroupConfigurer groupConfigurer(MockWebServer server) {
|
||||
return groups -> {
|
||||
|
||||
groups
|
||||
.forEachClient((group, builder) -> builder
|
||||
.baseUrl(server.url("").toString())
|
||||
.defaultHeader("Accept", "application/vnd.github.v3+json"));
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
MockWebServer mockServer() {
|
||||
return new MockWebServer();
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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 clients copy of the License at
|
||||
*
|
||||
* https://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.security.docs.features.integrations.rest.configurationrestclient;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider;
|
||||
import org.springframework.security.docs.features.integrations.rest.clientregistrationid.UserService;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
/**
|
||||
* Tests RestClient configuration for HTTP Interface clients.
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = RestClientHttpInterfaceIntegrationConfiguration.class)
|
||||
class RestClientHttpInterfaceIntegrationConfigurationTests {
|
||||
|
||||
@Test
|
||||
void getAuthenticatedUser(@Autowired MockWebServer webServer, @Autowired OAuth2AuthorizedClientManager authorizedClients, @Autowired UserService users)
|
||||
throws InterruptedException {
|
||||
ClientRegistration registration = CommonOAuth2Provider.GITHUB.getBuilder("github").clientId("github").build();
|
||||
|
||||
Instant issuedAt = Instant.now();
|
||||
Instant expiresAt = issuedAt.plus(Duration.ofMinutes(5));
|
||||
OAuth2AccessToken token = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "1234",
|
||||
issuedAt, expiresAt);
|
||||
OAuth2AuthorizedClient result = new OAuth2AuthorizedClient(registration, "rob", token);
|
||||
given(authorizedClients.authorize(any())).willReturn(result);
|
||||
|
||||
webServer.enqueue(new MockResponse().addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(
|
||||
"""
|
||||
{"login": "rob_winch", "id": 1234, "name": "Rob Winch" }
|
||||
"""));
|
||||
|
||||
users.getAuthenticatedUser();
|
||||
|
||||
assertThat(webServer.takeRequest().getHeader(HttpHeaders.AUTHORIZATION)).isEqualTo("Bearer " + token.getTokenValue());
|
||||
}
|
||||
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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 clients copy of the License at
|
||||
*
|
||||
* https://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.security.docs.features.integrations.rest.configurationwebclient;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider;
|
||||
import org.springframework.security.docs.features.integrations.rest.clientregistrationid.UserService;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
/**
|
||||
* Demonstrates configuring RestClient with interface based proxy clients.
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = ServerWebClientHttpInterfaceIntegrationConfiguration.class)
|
||||
class ServerRestClientHttpInterfaceIntegrationConfigurationTests {
|
||||
|
||||
@Test
|
||||
void getAuthenticatedUser(@Autowired MockWebServer webServer, @Autowired ReactiveOAuth2AuthorizedClientManager authorizedClients, @Autowired UserService users)
|
||||
throws InterruptedException {
|
||||
ClientRegistration registration = CommonOAuth2Provider.GITHUB.getBuilder("github").clientId("github").build();
|
||||
|
||||
Instant issuedAt = Instant.now();
|
||||
Instant expiresAt = issuedAt.plus(Duration.ofMinutes(5));
|
||||
OAuth2AccessToken token = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "1234",
|
||||
issuedAt, expiresAt);
|
||||
OAuth2AuthorizedClient result = new OAuth2AuthorizedClient(registration, "rob", token);
|
||||
given(authorizedClients.authorize(any())).willReturn(Mono.just(result));
|
||||
|
||||
webServer.enqueue(new MockResponse().addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(
|
||||
"""
|
||||
{"login": "rob_winch", "id": 1234, "name": "Rob Winch" }
|
||||
"""));
|
||||
|
||||
users.getAuthenticatedUser();
|
||||
|
||||
assertThat(webServer.takeRequest().getHeader(HttpHeaders.AUTHORIZATION)).isEqualTo("Bearer " + token.getTokenValue());
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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 clients copy of the License at
|
||||
*
|
||||
* https://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.security.docs.features.integrations.rest.configurationwebclient;
|
||||
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.docs.features.integrations.rest.clientregistrationid.UserService;
|
||||
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.web.client.support.OAuth2RestClientHttpServiceGroupConfigurer;
|
||||
import org.springframework.security.oauth2.client.web.reactive.function.client.support.OAuth2WebClientHttpServiceGroupConfigurer;
|
||||
import org.springframework.web.reactive.function.client.support.WebClientHttpServiceGroupConfigurer;
|
||||
import org.springframework.web.service.registry.HttpServiceGroup;
|
||||
import org.springframework.web.service.registry.ImportHttpServices;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Documentation for {@link OAuth2RestClientHttpServiceGroupConfigurer}.
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ImportHttpServices(types = UserService.class, clientType = HttpServiceGroup.ClientType.WEB_CLIENT)
|
||||
public class ServerWebClientHttpInterfaceIntegrationConfiguration {
|
||||
|
||||
// tag::config[]
|
||||
@Bean
|
||||
OAuth2WebClientHttpServiceGroupConfigurer securityConfigurer(
|
||||
ReactiveOAuth2AuthorizedClientManager manager) {
|
||||
return OAuth2WebClientHttpServiceGroupConfigurer.from(manager);
|
||||
}
|
||||
// end::config[]
|
||||
|
||||
@Bean
|
||||
ReactiveOAuth2AuthorizedClientManager authorizedClientManager() {
|
||||
return mock(ReactiveOAuth2AuthorizedClientManager.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebClientHttpServiceGroupConfigurer groupConfigurer(MockWebServer server) {
|
||||
return groups -> {
|
||||
String baseUrl = server.url("").toString();
|
||||
groups
|
||||
.forEachClient((group, builder) -> builder
|
||||
.baseUrl(baseUrl)
|
||||
.defaultHeader("Accept", "application/vnd.github.v3+json"));
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
MockWebServer mockServer() {
|
||||
return new MockWebServer();
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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 clients copy of the License at
|
||||
*
|
||||
* https://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.security.kt.docs.features.integrations.rest.clientregistrationid
|
||||
|
||||
|
||||
/**
|
||||
* A user.
|
||||
* @param login
|
||||
* @param id
|
||||
* @param name
|
||||
* @author Rob Winch
|
||||
* @see UserService
|
||||
*/
|
||||
@JvmRecord
|
||||
data class User(val login: String, val id: Int, val name: String)
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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 clients copy of the License at
|
||||
*
|
||||
* https://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.security.kt.docs.features.integrations.rest.clientregistrationid
|
||||
|
||||
import org.springframework.security.oauth2.client.annotation.ClientRegistrationId
|
||||
import org.springframework.web.service.annotation.GetExchange
|
||||
import org.springframework.web.service.annotation.HttpExchange
|
||||
|
||||
/**
|
||||
* Demonstrates a service for {@link ClientRegistrationId} and HTTP Interface clients.
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@HttpExchange
|
||||
interface UserService {
|
||||
|
||||
// tag::getAuthenticatedUser[]
|
||||
@GetExchange("/user")
|
||||
@ClientRegistrationId("github")
|
||||
fun getAuthenticatedUser() : User
|
||||
// end::getAuthenticatedUser[]
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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 clients copy of the License at
|
||||
*
|
||||
* https://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.security.kt.docs.features.integrations.rest.configurationrestclient
|
||||
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.mockito.Mockito
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.kt.docs.features.integrations.rest.clientregistrationid.UserService
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager
|
||||
import org.springframework.security.oauth2.client.web.client.support.OAuth2RestClientHttpServiceGroupConfigurer
|
||||
import org.springframework.web.client.RestClient
|
||||
import org.springframework.web.client.support.RestClientHttpServiceGroupConfigurer
|
||||
import org.springframework.web.service.registry.HttpServiceGroup
|
||||
import org.springframework.web.service.registry.HttpServiceGroupConfigurer
|
||||
import org.springframework.web.service.registry.HttpServiceGroupConfigurer.ClientCallback
|
||||
import org.springframework.web.service.registry.ImportHttpServices
|
||||
|
||||
/**
|
||||
* Documentation for [OAuth2RestClientHttpServiceGroupConfigurer].
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ImportHttpServices(types = [UserService::class])
|
||||
class RestClientHttpInterfaceIntegrationConfiguration {
|
||||
// tag::config[]
|
||||
@Bean
|
||||
fun securityConfigurer(manager: OAuth2AuthorizedClientManager): OAuth2RestClientHttpServiceGroupConfigurer {
|
||||
return OAuth2RestClientHttpServiceGroupConfigurer.from(manager)
|
||||
}
|
||||
// end::config[]
|
||||
|
||||
@Bean
|
||||
fun authorizedClientManager(): OAuth2AuthorizedClientManager? {
|
||||
return Mockito.mock<OAuth2AuthorizedClientManager?>(OAuth2AuthorizedClientManager::class.java)
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun groupConfigurer(server: MockWebServer): RestClientHttpServiceGroupConfigurer {
|
||||
return RestClientHttpServiceGroupConfigurer { groups: HttpServiceGroupConfigurer.Groups<RestClient.Builder> ->
|
||||
groups.forEachClient(ClientCallback { group: HttpServiceGroup, builder: RestClient.Builder ->
|
||||
builder
|
||||
.baseUrl(server.url("").toString())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun mockServer(): MockWebServer {
|
||||
return MockWebServer()
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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 clients copy of the License at
|
||||
*
|
||||
* https://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.security.kt.docs.features.integrations.rest.configurationrestclient
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkObject
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider
|
||||
import org.springframework.security.kt.docs.features.integrations.rest.clientregistrationid.UserService
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
@ExtendWith(SpringExtension::class)
|
||||
@ContextConfiguration(classes = [RestClientHttpInterfaceIntegrationConfiguration::class])
|
||||
internal class RestClientHttpInterfaceIntegrationConfigurationTests {
|
||||
@Test
|
||||
fun getAuthenticatedUser(
|
||||
@Autowired webServer: MockWebServer,
|
||||
@Autowired authorizedClients: OAuth2AuthorizedClientManager,
|
||||
@Autowired users: UserService
|
||||
) {
|
||||
val registration = CommonOAuth2Provider.GITHUB.getBuilder("github").clientId("github").build()
|
||||
|
||||
val issuedAt = Instant.now()
|
||||
val expiresAt = issuedAt.plus(Duration.ofMinutes(5))
|
||||
val token = OAuth2AccessToken(
|
||||
OAuth2AccessToken.TokenType.BEARER, "1234",
|
||||
issuedAt, expiresAt
|
||||
)
|
||||
val result = OAuth2AuthorizedClient(registration, "rob", token)
|
||||
mockkObject(authorizedClients)
|
||||
every {
|
||||
authorizedClients.authorize(any())
|
||||
} returns result
|
||||
|
||||
webServer.enqueue(
|
||||
MockResponse().addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(
|
||||
"""
|
||||
{"login": "rob_winch", "id": 1234, "name": "Rob Winch" }
|
||||
""".trimIndent()
|
||||
)
|
||||
)
|
||||
|
||||
users.getAuthenticatedUser()
|
||||
|
||||
Assertions.assertThat(webServer.takeRequest().getHeader(HttpHeaders.AUTHORIZATION))
|
||||
.isEqualTo("Bearer " + token.getTokenValue())
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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 clients copy of the License at
|
||||
*
|
||||
* https://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.security.kt.docs.features.integrations.rest.configurationwebclient
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkObject
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.extension.ExtendWith
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.http.HttpHeaders
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider
|
||||
import org.springframework.security.kt.docs.features.integrations.rest.clientregistrationid.UserService
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient
|
||||
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension
|
||||
import reactor.core.publisher.Mono
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
@ExtendWith(SpringExtension::class)
|
||||
@ContextConfiguration(classes = [ServerWebClientHttpInterfaceIntegrationConfiguration::class])
|
||||
internal class ServerRestClientHttpInterfaceIntegrationConfigurationTests {
|
||||
@Test
|
||||
@Throws(InterruptedException::class)
|
||||
fun getAuthenticatedUser(
|
||||
@Autowired webServer: MockWebServer,
|
||||
@Autowired authorizedClients: ReactiveOAuth2AuthorizedClientManager,
|
||||
@Autowired users: UserService
|
||||
) {
|
||||
val registration = CommonOAuth2Provider.GITHUB.getBuilder("github").clientId("github").build()
|
||||
|
||||
val issuedAt = Instant.now()
|
||||
val expiresAt = issuedAt.plus(Duration.ofMinutes(5))
|
||||
val token = OAuth2AccessToken(
|
||||
OAuth2AccessToken.TokenType.BEARER, "1234",
|
||||
issuedAt, expiresAt
|
||||
)
|
||||
val result = OAuth2AuthorizedClient(registration, "rob", token)
|
||||
mockkObject(authorizedClients)
|
||||
every {
|
||||
authorizedClients.authorize(any())
|
||||
} returns Mono.just(result)
|
||||
|
||||
webServer.enqueue(
|
||||
MockResponse().addHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(
|
||||
"""
|
||||
{"login": "rob_winch", "id": 1234, "name": "Rob Winch" }
|
||||
|
||||
""".trimIndent()
|
||||
)
|
||||
)
|
||||
|
||||
users.getAuthenticatedUser()
|
||||
|
||||
Assertions.assertThat(webServer.takeRequest().getHeader(HttpHeaders.AUTHORIZATION))
|
||||
.isEqualTo("Bearer " + token.getTokenValue())
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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 clients copy of the License at
|
||||
*
|
||||
* https://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.security.kt.docs.features.integrations.rest.configurationwebclient
|
||||
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.mockito.Mockito
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.security.kt.docs.features.integrations.rest.clientregistrationid.UserService
|
||||
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager
|
||||
import org.springframework.security.oauth2.client.web.client.support.OAuth2RestClientHttpServiceGroupConfigurer
|
||||
import org.springframework.security.oauth2.client.web.reactive.function.client.support.OAuth2WebClientHttpServiceGroupConfigurer
|
||||
import org.springframework.web.reactive.function.client.WebClient
|
||||
import org.springframework.web.reactive.function.client.support.WebClientHttpServiceGroupConfigurer
|
||||
import org.springframework.web.service.registry.HttpServiceGroup
|
||||
import org.springframework.web.service.registry.HttpServiceGroupConfigurer
|
||||
import org.springframework.web.service.registry.HttpServiceGroupConfigurer.ClientCallback
|
||||
import org.springframework.web.service.registry.ImportHttpServices
|
||||
|
||||
/**
|
||||
* Documentation for [OAuth2RestClientHttpServiceGroupConfigurer].
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ImportHttpServices(types = [UserService::class], clientType = HttpServiceGroup.ClientType.WEB_CLIENT)
|
||||
class ServerWebClientHttpInterfaceIntegrationConfiguration {
|
||||
// tag::config[]
|
||||
@Bean
|
||||
fun securityConfigurer(
|
||||
manager: ReactiveOAuth2AuthorizedClientManager?
|
||||
): OAuth2WebClientHttpServiceGroupConfigurer {
|
||||
return OAuth2WebClientHttpServiceGroupConfigurer.from(manager)
|
||||
}
|
||||
|
||||
// end::config[]
|
||||
@Bean
|
||||
fun authorizedClientManager(): ReactiveOAuth2AuthorizedClientManager? {
|
||||
return Mockito.mock<ReactiveOAuth2AuthorizedClientManager?>(ReactiveOAuth2AuthorizedClientManager::class.java)
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun groupConfigurer(server: MockWebServer): WebClientHttpServiceGroupConfigurer {
|
||||
return WebClientHttpServiceGroupConfigurer { groups: HttpServiceGroupConfigurer.Groups<WebClient.Builder?>? ->
|
||||
val baseUrl = server.url("").toString()
|
||||
groups!!
|
||||
.forEachClient(ClientCallback { group: HttpServiceGroup?, builder: WebClient.Builder? ->
|
||||
builder!!
|
||||
.baseUrl(baseUrl)
|
||||
.defaultHeader("Accept", "application/vnd.github.v3+json")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun mockServer(): MockWebServer {
|
||||
return MockWebServer()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user