Skip to content

Commit 711c1fb

Browse files
committed
OAuth2 auto configuration support for Eureka Client.
1 parent 06398da commit 711c1fb

20 files changed

+342
-64
lines changed

docs/src/main/asciidoc/spring-cloud-netflix.adoc

+25-4
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,35 @@ See {github-code}/spring-cloud-netflix-eureka-client/src/main/java/org/springfra
8585

8686
To disable the Eureka Discovery Client you can set `eureka.client.enabled` to `false`.
8787

88-
=== Authenticating with the Eureka Server
88+
=== Basic authentication with the Eureka Server
8989

9090
HTTP basic authentication will be automatically added to your eureka
9191
client if one of the `eureka.client.serviceUrl.defaultZone` URLs has
9292
credentials embedded in it (curl style, like
93-
`http://user:password@localhost:8761/eureka`). For more complex needs
94-
you can create a `@Bean` of type `DiscoveryClientOptionalArgs` and
95-
inject `ClientFilter` instances into it, all of which will be applied
93+
`http://user:password@localhost:8761/eureka`).
94+
95+
=== OAuth2 client support
96+
97+
OAuth 2 support will be auto configured when `org.springframework.security.oauth:spring-security-oauth2`
98+
is available in your classpath and you provide a `@Bean` of type `EurekaOAuth2ResourceDetails` within
99+
your context. OAuth2 resource details can by configured with the following properties:
100+
101+
.application.yml
102+
----
103+
eureka:
104+
client:
105+
oauth2:
106+
client_secret: client-secret
107+
client_id: user
108+
access_token_uri: oauth2-token-uri
109+
----
110+
111+
IMPORTANT: OAuth2 client support is only supported with Rest Template. Jersey dependencies must be excluded. See <<EurekaClient without Jersey>> for more information.
112+
113+
=== Custom authentication
114+
115+
For more complex needs you can create a `@Bean` of type `DiscoveryClientOptionalArgs` and
116+
inject `ClientFilter` or `RestTemplace` instances into it, all of which will be applied
96117
to the calls from the client to the server.
97118

98119
NOTE: Because of a limitation in Eureka it isn't possible to support

pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<spring-cloud-commons.version>2.0.0.BUILD-SNAPSHOT</spring-cloud-commons.version>
2828
<spring-cloud-config.version>2.0.0.BUILD-SNAPSHOT</spring-cloud-config.version>
2929
<spring-cloud-stream.version>Elmhurst.BUILD-SNAPSHOT</spring-cloud-stream.version>
30+
<spring-security-oauth2.version>2.2.1.RELEASE</spring-security-oauth2.version>
3031
<!-- Has to be a stable version (not one that depends on this version of netflix): -->
3132
<donotreplacespring-cloud-contract.version>1.2.0.RELEASE</donotreplacespring-cloud-contract.version>
3233

@@ -119,6 +120,11 @@
119120
<type>pom</type>
120121
<scope>import</scope>
121122
</dependency>
123+
<dependency>
124+
<groupId>org.springframework.security.oauth</groupId>
125+
<artifactId>spring-security-oauth2</artifactId>
126+
<version>${spring-security-oauth2.version}</version>
127+
</dependency>
122128
<dependency>
123129
<groupId>io.netty</groupId>
124130
<artifactId>netty-codec-http</artifactId>

spring-cloud-netflix-eureka-client/pom.xml

+6
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@
111111
<artifactId>ribbon-httpclient</artifactId>
112112
<optional>true</optional>
113113
</dependency>
114+
<dependency>
115+
<groupId>org.springframework.security.oauth</groupId>
116+
<artifactId>spring-security-oauth2</artifactId>
117+
<optional>true</optional>
118+
</dependency>
119+
114120
<dependency>
115121
<groupId>org.springframework.boot</groupId>
116122
<artifactId>spring-boot-starter-security</artifactId>

spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java

+32-2
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@
1616

1717
package org.springframework.cloud.netflix.eureka.config;
1818

19+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
1920
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2021
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2122
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
2223
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
2324
import org.springframework.cloud.netflix.eureka.MutableDiscoveryClientOptionalArgs;
25+
import org.springframework.cloud.netflix.eureka.http.BasicEurekaRestTemplateFactory;
26+
import org.springframework.cloud.netflix.eureka.http.EurekaRestTemplateFactory;
2427
import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs;
28+
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactories;
29+
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactory;
2530
import org.springframework.context.annotation.Bean;
2631
import org.springframework.context.annotation.Configuration;
2732

@@ -31,12 +36,37 @@
3136
* @author Daniel Lavoie
3237
*/
3338
@Configuration
39+
@AutoConfigureAfter(EurekaOAuth2AutoConfiguration.class)
3440
public class DiscoveryClientOptionalArgsConfiguration {
41+
42+
@Bean
43+
@ConditionalOnMissingBean(value = EurekaRestTemplateFactory.class)
44+
public EurekaRestTemplateFactory eurekaRestTemplateFactory() {
45+
return new BasicEurekaRestTemplateFactory();
46+
}
47+
48+
@Bean
49+
@ConditionalOnMissingBean
50+
public RestTemplateTransportClientFactory restTemplateTransportClientFactory(
51+
EurekaRestTemplateFactory eurekaRestTemplateFactory) {
52+
return new RestTemplateTransportClientFactory(eurekaRestTemplateFactory);
53+
}
54+
55+
@Bean
56+
@ConditionalOnMissingBean
57+
public RestTemplateTransportClientFactories restTemplateTransportClientFactories(
58+
RestTemplateTransportClientFactory restTemplateTransportClientFactory) {
59+
return new RestTemplateTransportClientFactories(
60+
restTemplateTransportClientFactory);
61+
}
62+
3563
@Bean
3664
@ConditionalOnMissingClass("com.sun.jersey.api.client.filter.ClientFilter")
3765
@ConditionalOnMissingBean(value = AbstractDiscoveryClientOptionalArgs.class, search = SearchStrategy.CURRENT)
38-
public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs() {
39-
return new RestTemplateDiscoveryClientOptionalArgs();
66+
public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs(
67+
RestTemplateTransportClientFactories restTemplateTransportClientFactories) {
68+
return new RestTemplateDiscoveryClientOptionalArgs(
69+
restTemplateTransportClientFactories);
4070
}
4171

4272
@Bean
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.springframework.cloud.netflix.eureka.config;
2+
3+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
4+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
5+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
6+
import org.springframework.cloud.netflix.eureka.http.EurekaRestTemplateFactory;
7+
import org.springframework.cloud.netflix.eureka.http.oauth2.EurekaOAuth2ResourceDetails;
8+
import org.springframework.cloud.netflix.eureka.http.oauth2.OAuth2EurekaRestTemplateFactory;
9+
import org.springframework.context.annotation.Bean;
10+
import org.springframework.context.annotation.Configuration;
11+
import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails;
12+
13+
@Configuration
14+
@ConditionalOnClass(BaseOAuth2ProtectedResourceDetails.class)
15+
public class EurekaOAuth2AutoConfiguration {
16+
@Bean
17+
@ConditionalOnProperty("eureka.client.oauth2.client-id")
18+
public EurekaOAuth2ResourceDetails eurekaOAuth2ResourceDetails() {
19+
return new EurekaOAuth2ResourceDetails();
20+
}
21+
22+
@Bean
23+
@ConditionalOnBean(EurekaOAuth2ResourceDetails.class)
24+
public EurekaRestTemplateFactory eurekaRestTemplateFactory(
25+
EurekaOAuth2ResourceDetails eurekaOAuth2ResourceDetails) {
26+
return new OAuth2EurekaRestTemplateFactory(eurekaOAuth2ResourceDetails);
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2017 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+
* http://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.cloud.netflix.eureka.http;
18+
19+
import java.net.URI;
20+
import java.net.URISyntaxException;
21+
22+
import org.springframework.boot.web.client.RestTemplateBuilder;
23+
import org.springframework.http.client.support.BasicAuthorizationInterceptor;
24+
import org.springframework.web.client.RestTemplate;
25+
26+
/**
27+
* @author Daniel Lavoie
28+
*/
29+
public class BasicEurekaRestTemplateFactory implements EurekaRestTemplateFactory {
30+
@Override
31+
public RestTemplate newRestTemplate(String serviceUrl) {
32+
RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
33+
try {
34+
URI serviceURI = new URI(serviceUrl);
35+
if (serviceURI.getUserInfo() != null) {
36+
String[] credentials = serviceURI.getUserInfo().split(":");
37+
if (credentials.length == 2) {
38+
restTemplateBuilder.interceptors(new BasicAuthorizationInterceptor(
39+
credentials[0], credentials[1]));
40+
}
41+
}
42+
}
43+
catch (URISyntaxException ignore) {
44+
45+
}
46+
47+
return restTemplateBuilder.build();
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2017 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+
* http://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.cloud.netflix.eureka.http;
18+
19+
import org.springframework.web.client.RestTemplate;
20+
21+
/**
22+
* @author Daniel Lavoie
23+
*/
24+
public interface EurekaRestTemplateFactory {
25+
RestTemplate newRestTemplate(String serviceUrl);
26+
}

spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateDiscoveryClientOptionalArgs.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@
1919
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
2020

2121
/**
22+
* Eureka client extension that allows customization of the transport client.
23+
*
2224
* @author Daniel Lavoie
2325
*/
2426
public class RestTemplateDiscoveryClientOptionalArgs
2527
extends AbstractDiscoveryClientOptionalArgs<Void> {
26-
public RestTemplateDiscoveryClientOptionalArgs() {
27-
setTransportClientFactories(new RestTemplateTransportClientFactories());
28+
public RestTemplateDiscoveryClientOptionalArgs(
29+
RestTemplateTransportClientFactories restTemplateTransportClientFactories) {
30+
setTransportClientFactories(restTemplateTransportClientFactories);
2831
}
2932
}

spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateEurekaHttpClient.java

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import com.netflix.discovery.util.StringUtil;
4545

4646
/**
47+
* {@link RestTemplate} based implementation of an {@link EurekaHttpClient}.
48+
*
4749
* @author Daniel Lavoie
4850
*/
4951
public class RestTemplateEurekaHttpClient implements EurekaHttpClient {

spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/RestTemplateTransportClientFactories.java

+13-7
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@
3333
*/
3434
public class RestTemplateTransportClientFactories
3535
implements TransportClientFactories<Void> {
36+
private final RestTemplateTransportClientFactory restTemplateTransportClientFactory;
37+
38+
public RestTemplateTransportClientFactories(
39+
RestTemplateTransportClientFactory restTemplateTransportClientFactory) {
40+
this.restTemplateTransportClientFactory = restTemplateTransportClientFactory;
41+
}
3642

3743
@Override
3844
public TransportClientFactory newTransportClientFactory(
@@ -44,15 +50,15 @@ public TransportClientFactory newTransportClientFactory(
4450
public TransportClientFactory newTransportClientFactory(
4551
EurekaClientConfig clientConfig, Collection<Void> additionalFilters,
4652
InstanceInfo myInstanceInfo) {
47-
return new RestTemplateTransportClientFactory();
53+
return restTemplateTransportClientFactory;
4854
}
4955

5056
@Override
51-
public TransportClientFactory newTransportClientFactory(final EurekaClientConfig clientConfig,
52-
final Collection<Void> additionalFilters,
53-
final InstanceInfo myInstanceInfo,
54-
final Optional<SSLContext> sslContext,
55-
final Optional<HostnameVerifier> hostnameVerifier) {
56-
return new RestTemplateTransportClientFactory();
57+
public TransportClientFactory newTransportClientFactory(
58+
final EurekaClientConfig clientConfig,
59+
final Collection<Void> additionalFilters, final InstanceInfo myInstanceInfo,
60+
final Optional<SSLContext> sslContext,
61+
final Optional<HostnameVerifier> hostnameVerifier) {
62+
return restTemplateTransportClientFactory;
5763
}
5864
}

0 commit comments

Comments
 (0)