This is an automated email from the ASF dual-hosted git repository.
liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git
The following commit(s) were added to refs/heads/master by this push:
new be1ff21 [SCB-2089] 支持服务中心rbac认证 (#2000)
be1ff21 is described below
commit be1ff217423dbba58c6c453dcb856f7a8c92d3ce
Author: 兴 <[email protected]>
AuthorDate: Thu Oct 15 09:13:00 2020 +0800
[SCB-2089] 支持服务中心rbac认证 (#2000)
Co-authored-by: yhs0092 <[email protected]>
---
.../config/client/ConfigCenterClient.java | 6 +-
.../apache/servicecomb/foundation/auth/Cipher.java | 24 +++
.../servicecomb/foundation/auth/DefaultCipher.java | 41 +++++
.../servicecomb/serviceregistry/RegistryUtils.java | 8 +
.../servicecomb/serviceregistry/api/Const.java | 3 +-
.../api/request/RbacTokenRequest.java | 52 ++++++
.../api/response/RbacTokenResponse.java | 49 ++++++
.../auth/TokenAuthHeaderProvider.java | 90 ++++++++++
.../serviceregistry/auth/TokenCacheManager.java | 194 +++++++++++++++++++++
.../client/ServiceRegistryClient.java | 8 +-
.../client/http/ServiceRegistryClientImpl.java | 45 ++++-
....servicecomb.foundation.auth.AuthHeaderProvider | 1 +
.../client/LocalServiceRegistryClientImpl.java | 7 +
.../client/http/TestServiceRegistryClientImpl.java | 103 +++++++++++
14 files changed, 621 insertions(+), 10 deletions(-)
diff --git
a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/client/ConfigCenterClient.java
b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/client/ConfigCenterClient.java
index 397a3b1..622c6c2 100644
---
a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/client/ConfigCenterClient.java
+++
b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/client/ConfigCenterClient.java
@@ -31,7 +31,6 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.ServiceLoader;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -45,6 +44,7 @@ import
org.apache.servicecomb.foundation.common.event.EventManager;
import org.apache.servicecomb.foundation.common.net.IpPort;
import org.apache.servicecomb.foundation.common.net.NetUtils;
import org.apache.servicecomb.foundation.common.utils.JsonUtils;
+import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
import
org.apache.servicecomb.foundation.vertx.client.http.HttpClientWithContext;
import org.apache.servicecomb.foundation.vertx.client.http.HttpClients;
import org.slf4j.Logger;
@@ -95,8 +95,8 @@ public class ConfigCenterClient {
private boolean isWatching = false;
- private final ServiceLoader<AuthHeaderProvider> authHeaderProviders =
- ServiceLoader.load(AuthHeaderProvider.class);
+ private final List<AuthHeaderProvider> authHeaderProviders =
+ SPIServiceUtils.getSortedService(AuthHeaderProvider.class);
private URIConst uriConst = new URIConst();
diff --git
a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/Cipher.java
b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/Cipher.java
new file mode 100644
index 0000000..3912ce3
--- /dev/null
+++
b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/Cipher.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.servicecomb.foundation.auth;
+
+public interface Cipher {
+ String name();
+
+ char[] decrypt(char[] encrypted);
+}
diff --git
a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/DefaultCipher.java
b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/DefaultCipher.java
new file mode 100644
index 0000000..7138cca
--- /dev/null
+++
b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/DefaultCipher.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.servicecomb.foundation.auth;
+
+public final class DefaultCipher implements Cipher {
+ public static final String DEFAULT_CYPHER = "default";
+
+ private static final DefaultCipher INSTANCE = new DefaultCipher();
+
+ public static DefaultCipher getInstance() {
+ return INSTANCE;
+ }
+
+ private DefaultCipher() {
+ }
+
+ @Override
+ public String name() {
+ return DEFAULT_CYPHER;
+ }
+
+ @Override
+ public char[] decrypt(char[] encrypted) {
+ return encrypted;
+ }
+}
diff --git
a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/RegistryUtils.java
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/RegistryUtils.java
index b581de8..c2eb750 100644
---
a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/RegistryUtils.java
+++
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/RegistryUtils.java
@@ -271,6 +271,14 @@ public final class RegistryUtils {
}
}
+ public static ServiceRegistry getServiceRegistry(String registryName) {
+ if (ServiceRegistry.DEFAULT_REGISTRY_NAME.equals(registryName)) {
+ return getServiceRegistry();
+ }
+
+ return EXTRA_SERVICE_REGISTRIES.get(registryName);
+ }
+
public static class AfterServiceInstanceRegistryHandler {
private static AtomicInteger instanceRegisterCounter = new
AtomicInteger(EXTRA_SERVICE_REGISTRIES.size() + 1);
diff --git
a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/Const.java
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/Const.java
index 55c68cc..d2ff215 100644
---
a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/Const.java
+++
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/Const.java
@@ -177,13 +177,14 @@ public final class Const {
MICROSERVICE_INSTANCE_STATUS = V4_PREFIX +
"/microservices/%s/instances/%s/status";
}
}
+
+ public static final String RBAC_TOKEN = "/v4/token";
}
public static final String REGISTRY_APP_ID = "default";
public static final String REGISTRY_SERVICE_NAME = "SERVICECENTER";
-
public static final String PATH_CHECKSESSION = "checksession";
public static final int SERVICE_CENTER_ORDER = 100;
diff --git
a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/request/RbacTokenRequest.java
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/request/RbacTokenRequest.java
new file mode 100644
index 0000000..fa6ee71
--- /dev/null
+++
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/request/RbacTokenRequest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.servicecomb.serviceregistry.api.request;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class RbacTokenRequest {
+ @JsonProperty("name")
+ private String accountName;
+
+ private String password;
+
+ public String getAccountName() {
+ return accountName;
+ }
+
+ public void setAccountName(String accountName) {
+ this.accountName = accountName;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("RbacTokenRequest{");
+ sb.append("accountName='").append(accountName).append('\'');
+ sb.append(", password='").append(password).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git
a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/response/RbacTokenResponse.java
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/response/RbacTokenResponse.java
new file mode 100644
index 0000000..40ecadc
--- /dev/null
+++
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/api/response/RbacTokenResponse.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.servicecomb.serviceregistry.api.response;
+
+public class RbacTokenResponse {
+ private transient int statusCode;
+
+ private String token;
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+
+ public void setStatusCode(int statusCode) {
+ this.statusCode = statusCode;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("RbacTokenResponse{");
+ sb.append("statusCode=").append(statusCode);
+ sb.append(", token='").append(token).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git
a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/auth/TokenAuthHeaderProvider.java
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/auth/TokenAuthHeaderProvider.java
new file mode 100644
index 0000000..7b82a0e
--- /dev/null
+++
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/auth/TokenAuthHeaderProvider.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.servicecomb.serviceregistry.auth;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.servicecomb.foundation.auth.AuthHeaderProvider;
+import org.apache.servicecomb.foundation.auth.Cipher;
+import org.apache.servicecomb.foundation.auth.DefaultCipher;
+import org.apache.servicecomb.foundation.common.utils.BeanUtils;
+import org.apache.servicecomb.serviceregistry.ServiceRegistry;
+
+import com.netflix.config.DynamicPropertyFactory;
+
+public class TokenAuthHeaderProvider implements AuthHeaderProvider {
+ public static final String ACCOUNT_NAME_KEY =
"servicecomb.credentials.account.name";
+
+ public static final String PASSWORD_KEY =
"servicecomb.credentials.account.password";
+
+ public static final String CIPHER_KEY = "servicecomb.credentials.cipher";
+
+ private String registryName;
+
+ private String accountName;
+
+ private String password;
+
+ private String cipherName;
+
+ public TokenAuthHeaderProvider() {
+ this.registryName = ServiceRegistry.DEFAULT_REGISTRY_NAME;
+ this.accountName = DynamicPropertyFactory.getInstance()
+ .getStringProperty(ACCOUNT_NAME_KEY, null).get();
+ this.password = DynamicPropertyFactory.getInstance()
+ .getStringProperty(PASSWORD_KEY, null).get();
+ this.cipherName = DynamicPropertyFactory.getInstance()
+ .getStringProperty(CIPHER_KEY, DefaultCipher.DEFAULT_CYPHER).get();
+ if (StringUtils.isNotEmpty(accountName)) {
+ TokenCacheManager.getInstance().addTokenCache(registryName, accountName,
password, getCipher());
+ }
+ }
+
+ public TokenAuthHeaderProvider(String registryName, String accountName,
String password, String cipherName) {
+ this.registryName = registryName;
+ this.accountName = accountName;
+ this.password = password;
+ this.cipherName = cipherName;
+ TokenCacheManager.getInstance().addTokenCache(this.registryName,
this.accountName, this.password, getCipher());
+ }
+
+ @Override
+ public Map<String, String> authHeaders() {
+ String token = TokenCacheManager.getInstance().getToken(registryName);
+ if (StringUtils.isEmpty(token)) {
+ return new HashMap<>();
+ }
+
+ HashMap<String, String> header = new HashMap<>();
+ header.put("Authorization", "Bearer " + token);
+ return Collections.unmodifiableMap(header);
+ }
+
+ private Cipher getCipher() {
+ if (DefaultCipher.DEFAULT_CYPHER.equals(cipherName)) {
+ return DefaultCipher.getInstance();
+ }
+
+ Map<String, Cipher> cipherBeans = BeanUtils.getBeansOfType(Cipher.class);
+ return cipherBeans.values().stream().filter(c ->
c.name().equals(cipherName)).findFirst()
+ .orElseThrow(() -> new IllegalArgumentException("failed to find cipher
named " + cipherName));
+ }
+}
diff --git
a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/auth/TokenCacheManager.java
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/auth/TokenCacheManager.java
new file mode 100644
index 0000000..248c25a
--- /dev/null
+++
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/auth/TokenCacheManager.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.servicecomb.serviceregistry.auth;
+
+import java.time.Clock;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.annotation.Nonnull;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.servicecomb.foundation.auth.Cipher;
+import
org.apache.servicecomb.foundation.common.concurrency.SuppressedRunnableWrapper;
+import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
+import org.apache.servicecomb.foundation.common.utils.TimeUtils;
+import org.apache.servicecomb.foundation.vertx.client.http.HttpClients;
+import org.apache.servicecomb.serviceregistry.RegistryUtils;
+import org.apache.servicecomb.serviceregistry.ServiceRegistry;
+import org.apache.servicecomb.serviceregistry.api.request.RbacTokenRequest;
+import org.apache.servicecomb.serviceregistry.api.response.RbacTokenResponse;
+import org.apache.servicecomb.serviceregistry.client.ServiceRegistryClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class TokenCacheManager {
+ private static final Logger LOGGER =
LoggerFactory.getLogger(TokenCacheManager.class);
+
+ private static final TokenCacheManager INSTANCE = new TokenCacheManager();
+
+ private Clock clock = TimeUtils.getSystemDefaultZoneClock();
+
+ private ScheduledExecutorService tokenCacheWorker;
+
+ private Map<String, TokenCache> tokenCacheMap;
+
+ public static TokenCacheManager getInstance() {
+ return INSTANCE;
+ }
+
+ private TokenCacheManager() {
+ tokenCacheWorker = Executors.newScheduledThreadPool(2, new ThreadFactory()
{
+ private final AtomicInteger threadIndexer = new AtomicInteger();
+
+ @Override
+ public Thread newThread(@Nonnull Runnable r) {
+ Thread thread = new Thread(r, "auth-token-cache-" +
threadIndexer.getAndIncrement());
+ thread.setDaemon(true);
+ return thread;
+ }
+ });
+ tokenCacheMap = new ConcurrentHashMapEx<>();
+ }
+
+ public void addTokenCache(String registryName, String accountName, String
password, Cipher cipher) {
+ Objects.requireNonNull(registryName, "registryName should not be null!");
+ if (tokenCacheMap.containsKey(registryName)) {
+ LOGGER.warn("duplicate token cache registration for
serviceRegistry[{}]", registryName);
+ return;
+ }
+
+ TokenCache tokenCache = new TokenCache(registryName, accountName,
password, cipher, this.clock);
+ tokenCache.setTokenCacheWorker(this.tokenCacheWorker);
+ tokenCacheMap.put(registryName, tokenCache);
+ HttpClients.load();
+ RegistryUtils.init();
+ tokenCache.refreshToken();
+ }
+
+ public String getToken(String registryName) {
+ return Optional.ofNullable(tokenCacheMap.get(registryName))
+ .map(TokenCache::getToken)
+ .orElse("");
+ }
+
+ public static class TokenCache {
+ private final String registryName;
+
+ private final String accountName;
+
+ private final String password;
+
+ private final Clock clock;
+
+ private String token;
+
+ private long nextRefreshTime;
+
+ /**
+ * The life cycle period of a token, in millisecond.
+ * After the {@code tokenLife} time since the token created, it should be
refreshed.
+ * <p>
+ * Default life time in sc is 30min, give 2min buffer
+ * </p>
+ */
+ private long tokenLife = TimeUnit.MINUTES.toMillis(30 - 2);
+
+ private ScheduledExecutorService tokenCacheWorker;
+
+ private Cipher cipher;
+
+ public TokenCache(String registryName, String accountName, String password,
+ Cipher cipher, Clock clock) {
+ this.registryName = registryName;
+ this.accountName = accountName;
+ this.password = password;
+ this.cipher = cipher;
+ this.clock = clock;
+ }
+
+ public String getToken() {
+ return token == null ? "" : token;
+ }
+
+ public void setTokenCacheWorker(ScheduledExecutorService tokenCacheWorker)
{
+ Objects.requireNonNull(tokenCacheWorker, "input tokenCacheWorker is
null");
+ if (this.tokenCacheWorker != null) {
+ throw new IllegalStateException("tokenCacheWorker already set!");
+ }
+
+ this.tokenCacheWorker = tokenCacheWorker;
+ startTokenRefreshTask();
+ }
+
+ private void startTokenRefreshTask() {
+ this.tokenCacheWorker.scheduleAtFixedRate(
+ new SuppressedRunnableWrapper(() -> {
+ if (isTokenOutdated()) {
+ refreshToken();
+ }
+ }),
+ 1,
+ 5,
+ TimeUnit.SECONDS);
+ }
+
+ private boolean isTokenOutdated() {
+ return clock.millis() > nextRefreshTime;
+ }
+
+ private void refreshToken() {
+ ServiceRegistry serviceRegistry =
RegistryUtils.getServiceRegistry(registryName);
+ ServiceRegistryClient serviceRegistryClient =
+ serviceRegistry == null ? null :
serviceRegistry.getServiceRegistryClient();
+ if ((serviceRegistry == null || serviceRegistryClient == null)
+ && ServiceRegistry.DEFAULT_REGISTRY_NAME.equals(registryName)) {
+ LOGGER.error("failed to get default serviceRegistry");
+ tokenCacheWorker.schedule( // retry after 1 second
+ this::refreshToken, 1, TimeUnit.SECONDS);
+ return;
+ }
+ RbacTokenRequest request = new RbacTokenRequest();
+ request.setAccountName(new
String(cipher.decrypt(accountName.toCharArray())));
+ request.setPassword(new String(cipher.decrypt(password.toCharArray())));
+ RbacTokenResponse rbacTokenResponse =
serviceRegistryClient.getRbacToken(request);
+ LOGGER.info("refresh token successfully {}",
rbacTokenResponse.getStatusCode());
+ if (StringUtils.isEmpty(this.token)) {
+ if (Status.UNAUTHORIZED.getStatusCode() ==
rbacTokenResponse.getStatusCode()) {
+ // password wrong, do not try anymore
+ LOGGER.warn("username or password may be wrong!");
+ this.tokenCacheWorker.shutdown();
+ } else if (Status.NOT_FOUND.getStatusCode() ==
rbacTokenResponse.getStatusCode()) {
+ // service center not support, do not try
+ LOGGER.warn("service center do not support rbac token, you should
not config account info");
+ this.tokenCacheWorker.shutdown();
+ }
+ }
+ this.token = rbacTokenResponse.getToken();
+ this.nextRefreshTime = clock.millis() + tokenLife;
+
+ }
+ }
+}
diff --git
a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/ServiceRegistryClient.java
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/ServiceRegistryClient.java
index 7b6cab7..ab028b1 100644
---
a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/ServiceRegistryClient.java
+++
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/ServiceRegistryClient.java
@@ -21,15 +21,17 @@ import java.util.List;
import java.util.Map;
import org.apache.servicecomb.foundation.vertx.AsyncResultCallback;
+import
org.apache.servicecomb.registry.api.event.MicroserviceInstanceChangedEvent;
import org.apache.servicecomb.registry.api.registry.Microservice;
import org.apache.servicecomb.registry.api.registry.MicroserviceInstance;
import org.apache.servicecomb.registry.api.registry.MicroserviceInstanceStatus;
+import org.apache.servicecomb.registry.api.registry.MicroserviceInstances;
import org.apache.servicecomb.serviceregistry.api.registry.ServiceCenterInfo;
+import org.apache.servicecomb.serviceregistry.api.request.RbacTokenRequest;
import org.apache.servicecomb.serviceregistry.api.response.GetSchemaResponse;
import org.apache.servicecomb.serviceregistry.api.response.HeartbeatResponse;
-import
org.apache.servicecomb.registry.api.event.MicroserviceInstanceChangedEvent;
+import org.apache.servicecomb.serviceregistry.api.response.RbacTokenResponse;
import org.apache.servicecomb.serviceregistry.client.http.Holder;
-import org.apache.servicecomb.registry.api.registry.MicroserviceInstances;
public interface ServiceRegistryClient {
void init();
@@ -202,4 +204,6 @@ public interface ServiceRegistryClient {
* @return whether this operation success
*/
boolean updateMicroserviceInstanceStatus(String microserviceId, String
instanceId, MicroserviceInstanceStatus status);
+
+ RbacTokenResponse getRbacToken(RbacTokenRequest request);
}
diff --git
a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java
index d4921bd..a96908d 100644
---
a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java
+++
b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java
@@ -35,19 +35,22 @@ import javax.ws.rs.core.Response.Status;
import org.apache.servicecomb.foundation.common.net.IpPort;
import org.apache.servicecomb.foundation.common.utils.JsonUtils;
import org.apache.servicecomb.foundation.vertx.AsyncResultCallback;
-import org.apache.servicecomb.serviceregistry.RegistryUtils;
-import org.apache.servicecomb.serviceregistry.api.Const;
+import
org.apache.servicecomb.registry.api.event.MicroserviceInstanceChangedEvent;
+import org.apache.servicecomb.registry.api.registry.FindInstancesResponse;
import org.apache.servicecomb.registry.api.registry.Microservice;
import org.apache.servicecomb.registry.api.registry.MicroserviceInstance;
import org.apache.servicecomb.registry.api.registry.MicroserviceInstanceStatus;
import org.apache.servicecomb.registry.api.registry.MicroserviceInstances;
+import org.apache.servicecomb.serviceregistry.RegistryUtils;
+import org.apache.servicecomb.serviceregistry.api.Const;
+import org.apache.servicecomb.serviceregistry.api.Const.REGISTRY_API;
import org.apache.servicecomb.serviceregistry.api.registry.ServiceCenterInfo;
import org.apache.servicecomb.serviceregistry.api.request.CreateSchemaRequest;
import org.apache.servicecomb.serviceregistry.api.request.CreateServiceRequest;
+import org.apache.servicecomb.serviceregistry.api.request.RbacTokenRequest;
import
org.apache.servicecomb.serviceregistry.api.request.RegisterInstanceRequest;
import
org.apache.servicecomb.serviceregistry.api.request.UpdatePropertiesRequest;
import
org.apache.servicecomb.serviceregistry.api.response.CreateServiceResponse;
-import org.apache.servicecomb.registry.api.registry.FindInstancesResponse;
import
org.apache.servicecomb.serviceregistry.api.response.GetAllServicesResponse;
import
org.apache.servicecomb.serviceregistry.api.response.GetExistenceResponse;
import
org.apache.servicecomb.serviceregistry.api.response.GetInstancesResponse;
@@ -55,8 +58,8 @@ import
org.apache.servicecomb.serviceregistry.api.response.GetSchemaResponse;
import org.apache.servicecomb.serviceregistry.api.response.GetSchemasResponse;
import org.apache.servicecomb.serviceregistry.api.response.GetServiceResponse;
import org.apache.servicecomb.serviceregistry.api.response.HeartbeatResponse;
-import
org.apache.servicecomb.registry.api.event.MicroserviceInstanceChangedEvent;
import
org.apache.servicecomb.serviceregistry.api.response.MicroserviceInstanceResponse;
+import org.apache.servicecomb.serviceregistry.api.response.RbacTokenResponse;
import
org.apache.servicecomb.serviceregistry.api.response.RegisterInstanceResponse;
import org.apache.servicecomb.serviceregistry.client.ClientException;
import org.apache.servicecomb.serviceregistry.client.IpPortManager;
@@ -67,6 +70,7 @@ import
org.apache.servicecomb.serviceregistry.task.MicroserviceInstanceHeartbeat
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
@@ -955,4 +959,37 @@ public final class ServiceRegistryClientImpl implements
ServiceRegistryClient {
ipPortManager.initAutoDiscovery();
}
}
+
+ @Override
+ public RbacTokenResponse getRbacToken(RbacTokenRequest request) {
+ Holder<RbacTokenResponse> holder = new Holder<>();
+ IpPort ipPort = ipPortManager.getAvailableAddress();
+
+ byte[] requestBody;
+ try {
+ requestBody = JsonUtils.writeValueAsBytes(request);
+ } catch (JsonProcessingException e) {
+ LOGGER.error("failed to write request as byte array");
+ return new RbacTokenResponse();
+ }
+
+ CountDownLatch countDownLatch = new CountDownLatch(1);
+ restClientUtil.post(ipPort, REGISTRY_API.RBAC_TOKEN,
+ new RequestParam().setBody(requestBody),
+ syncHandler(countDownLatch, RbacTokenResponse.class, holder));
+ try {
+ countDownLatch.await();
+ } catch (InterruptedException e) {
+ LOGGER.error("failed to wait for rbac token response", e);
+ }
+
+ if (holder.value != null) {
+ holder.value.setStatusCode(holder.getStatusCode());
+ return holder.value;
+ }
+
+ RbacTokenResponse response = new RbacTokenResponse();
+ response.setStatusCode(holder.getStatusCode());
+ return response;
+ }
}
diff --git
a/service-registry/registry-service-center/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.AuthHeaderProvider
b/service-registry/registry-service-center/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.AuthHeaderProvider
index ee17cf9..0992eee 100644
---
a/service-registry/registry-service-center/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.AuthHeaderProvider
+++
b/service-registry/registry-service-center/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.AuthHeaderProvider
@@ -16,3 +16,4 @@
#
org.apache.servicecomb.serviceregistry.client.http.EmptyAuthHeaderProvider
+org.apache.servicecomb.serviceregistry.auth.TokenAuthHeaderProvider
diff --git
a/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java
b/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java
index a63e9b2..4c7183e 100644
---
a/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java
+++
b/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java
@@ -43,8 +43,10 @@ import
org.apache.servicecomb.registry.version.VersionRuleUtils;
import org.apache.servicecomb.registry.version.VersionUtils;
import org.apache.servicecomb.serviceregistry.api.registry.ServiceCenterConfig;
import org.apache.servicecomb.serviceregistry.api.registry.ServiceCenterInfo;
+import org.apache.servicecomb.serviceregistry.api.request.RbacTokenRequest;
import org.apache.servicecomb.serviceregistry.api.response.GetSchemaResponse;
import org.apache.servicecomb.serviceregistry.api.response.HeartbeatResponse;
+import org.apache.servicecomb.serviceregistry.api.response.RbacTokenResponse;
import org.apache.servicecomb.serviceregistry.client.http.Holder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -455,4 +457,9 @@ public class LocalServiceRegistryClientImpl implements
ServiceRegistryClient {
microserviceInstance.setStatus(status);
return true;
}
+
+ @Override
+ public RbacTokenResponse getRbacToken(RbacTokenRequest request) {
+ return new RbacTokenResponse();
+ }
}
diff --git
a/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java
b/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java
index fffee24..87d022f 100644
---
a/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java
+++
b/service-registry/registry-service-center/src/test/java/org/apache/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java
@@ -20,6 +20,7 @@ package org.apache.servicecomb.serviceregistry.client.http;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.fail;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -35,6 +36,7 @@ import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.servicecomb.config.BootStrapProperties;
import org.apache.servicecomb.foundation.common.net.IpPort;
+import org.apache.servicecomb.foundation.common.utils.JsonUtils;
import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
import org.apache.servicecomb.foundation.vertx.client.http.HttpClients;
import org.apache.servicecomb.registry.api.registry.Microservice;
@@ -44,10 +46,12 @@ import
org.apache.servicecomb.registry.definition.DefinitionConst;
import org.apache.servicecomb.serviceregistry.RegistryUtils;
import org.apache.servicecomb.serviceregistry.api.registry.ServiceCenterConfig;
import org.apache.servicecomb.serviceregistry.api.registry.ServiceCenterInfo;
+import org.apache.servicecomb.serviceregistry.api.request.RbacTokenRequest;
import
org.apache.servicecomb.serviceregistry.api.response.GetExistenceResponse;
import org.apache.servicecomb.serviceregistry.api.response.GetSchemaResponse;
import org.apache.servicecomb.serviceregistry.api.response.GetSchemasResponse;
import org.apache.servicecomb.serviceregistry.api.response.GetServiceResponse;
+import org.apache.servicecomb.serviceregistry.api.response.RbacTokenResponse;
import org.apache.servicecomb.serviceregistry.client.ClientException;
import
org.apache.servicecomb.serviceregistry.client.http.ServiceRegistryClientImpl.ResponseWrapper;
import org.apache.servicecomb.serviceregistry.config.ServiceRegistryConfig;
@@ -624,4 +628,103 @@ public class TestServiceRegistryClientImpl {
private void shouldThrowException() {
fail("an exception is expected");
}
+
+ @Test
+ public void getRbacToken() {
+ mockForGetRbacToken(200, "{\"token\":\"test_token_content\"}");
+ RbacTokenRequest request = new RbacTokenRequest();
+ request.setAccountName("test_account_name");
+ request.setPassword("test_password");
+ RbacTokenResponse response = oClient.getRbacToken(request);
+
+ Assert.assertEquals(200, response.getStatusCode());
+ Assert.assertEquals("test_token_content", response.getToken());
+ }
+
+ @Test
+ public void getRbacToken_waiting_response_interrupted() {
+ InterruptedException e = new InterruptedException();
+ new MockUp<CountDownLatch>() {
+ @Mock
+ public void await() throws InterruptedException {
+ throw e;
+ }
+ };
+ new MockUp<RestClientUtil>() {
+ @Mock
+ void post(IpPort ipPort, String uri, RequestParam requestParam,
+ Handler<RestResponse> responseHandler) {
+ }
+ };
+ RbacTokenRequest request = new RbacTokenRequest();
+ request.setAccountName("test_account_name");
+ request.setPassword("test_password");
+
+ RbacTokenResponse response = oClient.getRbacToken(request);
+
+ Assert.assertEquals(0, response.getStatusCode());
+ Assert.assertNull(response.getToken());
+ }
+
+ @Test
+ public void getRbacToken_serialize_request_exception() {
+ mockForGetRbacToken(200, "{\"token\":\"test_token_content\"}");
+ RbacTokenRequest request = new RbacTokenRequest() {
+ @Override
+ public String getAccountName() {
+ throw new IllegalStateException("mock serialization error");
+ }
+ };
+ request.setAccountName("test_account_name");
+ request.setPassword("test_password");
+
+ RbacTokenResponse response = oClient.getRbacToken(request);
+
+ Assert.assertEquals(0, response.getStatusCode());
+ Assert.assertNull(response.getToken());
+ }
+
+ @Test
+ public void getRbacToken_auth_failed() {
+ mockForGetRbacToken(401, "{\"detail\":\"wrong user name or password\","
+ + "\"errorCode\":\"401002\","
+ + "\"errorMessage\":\"Request unauthorized\"}");
+ RbacTokenRequest request = new RbacTokenRequest();
+ request.setAccountName("test_account_name");
+ request.setPassword("test_password");
+ RbacTokenResponse response = oClient.getRbacToken(request);
+
+ Assert.assertEquals(401, response.getStatusCode());
+ Assert.assertNull(response.getToken());
+ }
+
+ private void mockForGetRbacToken(final int responseStatusCode, final String
responseBody) {
+ new MockUp<RestClientUtil>() {
+ @Mock
+ void post(IpPort ipPort, String uri, RequestParam requestParam,
Handler<RestResponse> responseHandler) {
+ Assert.assertEquals("/v4/token", uri);
+ try {
+ RbacTokenRequest rbacTokenRequest =
JsonUtils.readValue(requestParam.getBody(), RbacTokenRequest.class);
+ Assert.assertEquals("test_account_name",
rbacTokenRequest.getAccountName());
+ Assert.assertEquals("test_password", rbacTokenRequest.getPassword());
+ } catch (IOException e) {
+ fail("malformed request body");
+ e.printStackTrace();
+ }
+ HttpClientResponse httpClientResponse = new
MockUp<HttpClientResponse>() {
+ @Mock
+ int statusCode() {
+ return responseStatusCode;
+ }
+
+ @Mock
+ HttpClientResponse bodyHandler(Handler<Buffer> bodyHandler) {
+ bodyHandler.handle(Buffer.buffer(responseBody));
+ return null;
+ }
+ }.getMockInstance();
+ responseHandler.handle(new RestResponse(null, httpClientResponse));
+ }
+ };
+ }
}