Skip to content

Commit d500682

Browse files
authored
[#1510] Fixing the issue where microservices cannot be registered when enabling RBAC authentication in a dual-engine disaster recovery scenario. (#1511)
1 parent 4b0633d commit d500682

File tree

11 files changed

+262
-120
lines changed

11 files changed

+262
-120
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
import spring-framework-bom. No need configure spring version. -->
3939
<spring-cloud.version>2024.0.2</spring-cloud.version>
4040
<spring-boot.version>3.4.12</spring-boot.version>
41-
<servicecomb.version>2.8.27</servicecomb.version>
41+
<servicecomb.version>2.8.29</servicecomb.version>
4242
</properties>
4343

4444
<modules>

spring-cloud-huawei-service-engine/service-engine-common/src/main/java/com/huaweicloud/service/engine/common/disovery/ServiceCenterUtils.java

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.List;
2222
import java.util.Map;
2323

24+
import org.apache.commons.lang3.StringUtils;
2425
import org.apache.servicecomb.foundation.auth.AuthHeaderProvider;
2526
import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider;
2627
import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties;
@@ -32,30 +33,56 @@
3233
import org.springframework.core.env.Environment;
3334

3435
import com.huaweicloud.common.event.EventManager;
36+
import com.huaweicloud.common.util.URLUtil;
3537
import com.huaweicloud.service.engine.common.configration.bootstrap.DiscoveryBootstrapProperties;
3638
import com.huaweicloud.service.engine.common.configration.bootstrap.ServiceCombSSLProperties;
3739
import com.huaweicloud.service.engine.common.transport.TransportUtils;
38-
import com.huaweicloud.common.util.URLUtil;
3940

4041
public class ServiceCenterUtils {
4142
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterUtils.class);
4243

44+
private static volatile ServiceCenterAddressManager serviceAddressManager;
45+
4346
public static ServiceCenterAddressManager createAddressManager(DiscoveryBootstrapProperties discoveryProperties) {
44-
List<String> addresses = URLUtil.dealMultiUrl(discoveryProperties.getAddress());
45-
LOGGER.info("initialize discovery server={}", addresses);
46-
return new ServiceCenterAddressManager("default", addresses, EventManager.getEventBus());
47+
if (serviceAddressManager == null) {
48+
synchronized (ServiceCenterUtils.class) {
49+
if (serviceAddressManager == null) {
50+
List<String> addresses = URLUtil.dealMultiUrl(discoveryProperties.getAddress());
51+
LOGGER.info("initialize discovery server={}", addresses);
52+
serviceAddressManager = new ServiceCenterAddressManager("default", addresses,
53+
getDataCenterRegion(discoveryProperties), getDataCenterZone(discoveryProperties),
54+
EventManager.getEventBus());
55+
}
56+
}
57+
}
58+
return serviceAddressManager;
59+
}
60+
61+
private static String getDataCenterRegion(DiscoveryBootstrapProperties discoveryProperties) {
62+
if (discoveryProperties.getDatacenter() == null) {
63+
return "";
64+
}
65+
String region = discoveryProperties.getDatacenter().getRegion();
66+
return StringUtils.isEmpty(region) ? "" : region;
67+
}
68+
69+
private static String getDataCenterZone(DiscoveryBootstrapProperties discoveryProperties) {
70+
if (discoveryProperties.getDatacenter() == null) {
71+
return "";
72+
}
73+
String zone = discoveryProperties.getDatacenter().getAvailableZone();
74+
return StringUtils.isEmpty(zone) ? "" : zone;
4775
}
4876

4977
// add other headers needed for registration by new ServiceCenterClient(...)
5078
public static ServiceCenterClient serviceCenterClient(DiscoveryBootstrapProperties discoveryProperties,
51-
ServiceCombSSLProperties serviceCombSSLProperties, List<AuthHeaderProvider> authHeaderProviders,
52-
Environment env) {
79+
ServiceCombSSLProperties serviceCombSSLProperties, List<AuthHeaderProvider> authHeaderProviders, Environment env) {
5380
ServiceCenterAddressManager addressManager = createAddressManager(discoveryProperties);
5481
SSLProperties sslProperties = TransportUtils
5582
.createSSLProperties(addressManager.sslEnabled(), serviceCombSSLProperties);
5683
return new ServiceCenterClient(addressManager, sslProperties,
57-
getRequestAuthHeaderProvider(authHeaderProviders),
58-
"default", new HashMap<>(), env).setEventBus(EventManager.getEventBus());
84+
getRequestAuthHeaderProvider(authHeaderProviders), "default", new HashMap<>(), env)
85+
.setEventBus(EventManager.getEventBus());
5986
}
6087

6188
public static ServiceCenterWatch serviceCenterWatch(DiscoveryBootstrapProperties discoveryProperties,
@@ -71,8 +98,9 @@ public static ServiceCenterWatch serviceCenterWatch(DiscoveryBootstrapProperties
7198

7299
private static RequestAuthHeaderProvider getRequestAuthHeaderProvider(List<AuthHeaderProvider> authHeaderProviders) {
73100
return signRequest -> {
101+
String host = signRequest != null && signRequest.getEndpoint() != null ? signRequest.getEndpoint().getHost() : "";
74102
Map<String, String> headers = new HashMap<>();
75-
authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders()));
103+
authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders(host)));
76104
return headers;
77105
};
78106
}

spring-cloud-huawei-service-engine/service-engine-common/src/main/java/com/huaweicloud/service/engine/common/transport/AkSkRequestAuthHeaderProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public AkSkRequestAuthHeaderProvider(ServiceCombAkSkProperties serviceCombAkSkPr
4141
}
4242

4343
@Override
44-
public Map<String, String> authHeaders() {
44+
public Map<String, String> authHeaders(String host) {
4545
if (isAKSKNotEnabled(serviceCombAkSkProperties)) {
4646
return Collections.emptyMap();
4747
}

spring-cloud-huawei-service-engine/service-engine-common/src/main/java/com/huaweicloud/service/engine/common/transport/RBACRequestAuthHeaderProvider.java

Lines changed: 61 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package com.huaweicloud.service.engine.common.transport;
1919

20+
import java.net.URI;
2021
import java.util.Collections;
2122
import java.util.HashMap;
2223
import java.util.Map;
@@ -26,8 +27,7 @@
2627

2728
import org.apache.commons.lang3.StringUtils;
2829
import org.apache.servicecomb.foundation.auth.AuthHeaderProvider;
29-
import org.apache.servicecomb.http.client.event.EngineConnectChangedEvent;
30-
import org.apache.servicecomb.service.center.client.OperationEvents;
30+
import org.apache.servicecomb.http.client.event.OperationEvents;
3131
import org.apache.servicecomb.service.center.client.ServiceCenterClient;
3232
import org.apache.servicecomb.service.center.client.model.RbacTokenRequest;
3333
import org.apache.servicecomb.service.center.client.model.RbacTokenResponse;
@@ -41,13 +41,12 @@
4141
import com.google.common.eventbus.Subscribe;
4242
import com.google.common.util.concurrent.Futures;
4343
import com.google.common.util.concurrent.ListenableFuture;
44+
import com.huaweicloud.common.event.EventManager;
4445
import com.huaweicloud.service.engine.common.configration.bootstrap.BootstrapProperties;
4546
import com.huaweicloud.service.engine.common.configration.bootstrap.DiscoveryBootstrapProperties;
46-
import com.huaweicloud.service.engine.common.configration.bootstrap.MicroserviceProperties;
4747
import com.huaweicloud.service.engine.common.configration.bootstrap.ServiceCombRBACProperties;
4848
import com.huaweicloud.service.engine.common.configration.bootstrap.ServiceCombSSLProperties;
4949
import com.huaweicloud.service.engine.common.disovery.ServiceCenterUtils;
50-
import com.huaweicloud.common.event.EventManager;
5150

5251
import jakarta.ws.rs.core.Response.Status;
5352

@@ -59,37 +58,26 @@ public class RBACRequestAuthHeaderProvider implements AuthHeaderProvider {
5958
// e.g. not found: will query token after token expired period
6059
public static final String INVALID_TOKEN = "invalid";
6160

62-
private static final String UN_AUTHORIZED_CODE_HALF_OPEN = "401302";
63-
6461
public static final String CACHE_KEY = "token";
6562

6663
public static final String AUTH_HEADER = "Authorization";
6764

6865
private static final long TOKEN_REFRESH_TIME_IN_SECONDS = 20 * 60 * 1000;
6966

70-
private final DiscoveryBootstrapProperties discoveryProperties;
71-
72-
private final ServiceCombSSLProperties serviceCombSSLProperties;
67+
private static final Object LOCK = new Object();
7368

7469
private final ServiceCombRBACProperties serviceCombRBACProperties;
7570

76-
private final MicroserviceProperties microserviceProperties;
77-
7871
private ExecutorService executorService;
7972

8073
private LoadingCache<String, String> cache;
8174

82-
private String lastErrorCode = "401302";
83-
84-
private int lastStatusCode = 401;
85-
8675
private ServiceCenterClient serviceCenterClient;
8776

8877
public RBACRequestAuthHeaderProvider(BootstrapProperties bootstrapProperties, Environment env) {
89-
this.discoveryProperties = bootstrapProperties.getDiscoveryBootstrapProperties();
90-
this.serviceCombSSLProperties = bootstrapProperties.getServiceCombSSLProperties();
78+
DiscoveryBootstrapProperties discoveryProperties = bootstrapProperties.getDiscoveryBootstrapProperties();
79+
ServiceCombSSLProperties serviceCombSSLProperties = bootstrapProperties.getServiceCombSSLProperties();
9180
this.serviceCombRBACProperties = bootstrapProperties.getServiceCombRBACProperties();
92-
this.microserviceProperties = bootstrapProperties.getMicroserviceProperties();
9381

9482
if (enabled()) {
9583
serviceCenterClient = ServiceCenterUtils.serviceCenterClient(discoveryProperties,
@@ -98,38 +86,41 @@ public RBACRequestAuthHeaderProvider(BootstrapProperties bootstrapProperties, En
9886

9987
executorService = Executors.newFixedThreadPool(1, t -> new Thread(t, "rbac-executor"));
10088
cache = CacheBuilder.newBuilder()
101-
.maximumSize(1)
89+
.maximumSize(10)
10290
.refreshAfterWrite(refreshTime(), TimeUnit.MILLISECONDS)
10391
.build(new CacheLoader<String, String>() {
10492
@Override
10593
public String load(String key) {
106-
return createHeaders();
94+
return createHeaders(key);
10795
}
10896

10997
@Override
11098
public ListenableFuture<String> reload(String key, String oldValue) {
111-
return Futures.submit(() -> createHeaders(), executorService);
99+
return Futures.submit(() -> createHeaders(key), executorService);
112100
}
113101
});
114102
}
115103
}
116104

117105
@Subscribe
118-
public void onNotPermittedEvent(OperationEvents.UnAuthorizedOperationEvent event) {
119-
this.executorService.submit(this::retryRefresh);
106+
public void onUnAuthorizedOperationEvent(OperationEvents.UnAuthorizedOperationEvent event) {
107+
LOGGER.warn("address {} unAuthorized, refresh cache token!", event.getAddress());
108+
cache.refresh(getHostByAddress(event.getAddress()));
120109
}
121110

122-
@Subscribe
123-
public void onEngineConnectChangedEvent(EngineConnectChangedEvent event) {
124-
cache.refresh(CACHE_KEY);
111+
private static String getHostByAddress(String address) {
112+
try {
113+
URI uri = URI.create(address);
114+
return uri.getHost();
115+
} catch (Exception e) {
116+
LOGGER.error("get host by address [{}] error!", address, e);
117+
return CACHE_KEY;
118+
}
125119
}
126120

127-
protected String createHeaders() {
128-
LOGGER.info("start to create RBAC headers");
129-
130-
RbacTokenResponse rbacTokenResponse = callCreateHeaders();
131-
lastErrorCode = rbacTokenResponse.getErrorCode();
132-
lastStatusCode = rbacTokenResponse.getStatusCode();
121+
protected String createHeaders(String key) {
122+
LOGGER.info("start to create server [{}] RBAC headers", key);
123+
RbacTokenResponse rbacTokenResponse = callCreateHeaders(key);
133124

134125
if (Status.UNAUTHORIZED.getStatusCode() == rbacTokenResponse.getStatusCode()
135126
|| Status.FORBIDDEN.getStatusCode() == rbacTokenResponse.getStatusCode()) {
@@ -140,51 +131,68 @@ protected String createHeaders() {
140131
// service center not support, do not try
141132
LOGGER.warn("service center do not support RBAC token, you should not config account info");
142133
return INVALID_TOKEN;
134+
} else if (Status.INTERNAL_SERVER_ERROR.getStatusCode() == rbacTokenResponse.getStatusCode()) {
135+
// return null for server_error, so the token information can be re-fetched on the next call.
136+
// It will prompt 'CacheLoader returned null for key xxx'
137+
LOGGER.warn("service center query RBAC token error!");
138+
return null;
143139
}
144140

145-
LOGGER.info("refresh token successfully {}", rbacTokenResponse.getStatusCode());
141+
LOGGER.info("refresh server [{}] token successfully {}", key, rbacTokenResponse.getStatusCode());
146142
return rbacTokenResponse.getToken();
147143
}
148144

149-
protected RbacTokenResponse callCreateHeaders() {
145+
protected RbacTokenResponse callCreateHeaders(String host) {
150146
RbacTokenRequest request = new RbacTokenRequest();
151147
request.setName(serviceCombRBACProperties.getName());
152148
request.setPassword(serviceCombRBACProperties.getPassword());
153-
154-
return serviceCenterClient.queryToken(request);
149+
try {
150+
return serviceCenterClient.queryToken(request, host);
151+
} catch (Exception e) {
152+
LOGGER.error("query token from server [{}] error!", host, e);
153+
}
154+
RbacTokenResponse response = new RbacTokenResponse();
155+
response.setStatusCode(Status.INTERNAL_SERVER_ERROR.getStatusCode());
156+
return response;
155157
}
156158

157159
protected long refreshTime() {
158160
return TOKEN_REFRESH_TIME_IN_SECONDS;
159161
}
160162

163+
/**
164+
* Retrieve the corresponding engine token cache information based on the host in the request
165+
* to resolve cross-engine authentication issues in dual-engine disaster recovery scenarios.
166+
*
167+
* @param host host
168+
* @return token info
169+
*/
161170
@Override
162-
public Map<String, String> authHeaders() {
171+
public Map<String, String> authHeaders(String host) {
163172
if (!enabled()) {
164173
return Collections.emptyMap();
165174
}
166-
167-
try {
168-
String header = cache.get(CACHE_KEY);
169-
if (!StringUtils.isEmpty(header)) {
170-
Map<String, String> tokens = new HashMap<>(1);
171-
tokens.put(AUTH_HEADER, "Bearer " + header);
172-
return tokens;
175+
String address = host;
176+
if (StringUtils.isEmpty(address)) {
177+
address = CACHE_KEY;
178+
}
179+
synchronized (LOCK) {
180+
try {
181+
String header = cache.get(address);
182+
if (!StringUtils.isEmpty(header)) {
183+
Map<String, String> tokens = new HashMap<>(1);
184+
tokens.put(AUTH_HEADER, "Bearer " + header);
185+
return tokens;
186+
}
187+
} catch (Exception e) {
188+
LOGGER.error("Get auth headers failed", e);
173189
}
174-
} catch (Exception e) {
175-
LOGGER.error("Get auth headers failed", e);
190+
return Collections.emptyMap();
176191
}
177-
return Collections.emptyMap();
178192
}
179193

180194
private boolean enabled() {
181195
return !StringUtils.isEmpty(serviceCombRBACProperties.getName()) && !StringUtils
182196
.isEmpty(serviceCombRBACProperties.getPassword());
183197
}
184-
185-
private void retryRefresh() {
186-
if (Status.UNAUTHORIZED.getStatusCode() == lastStatusCode && UN_AUTHORIZED_CODE_HALF_OPEN.equals(lastErrorCode)) {
187-
cache.refresh(microserviceProperties.getName());
188-
}
189-
}
190198
}

0 commit comments

Comments
 (0)