diff --git a/core/src/main/java/io/servicecomb/core/Const.java b/core/src/main/java/io/servicecomb/core/Const.java index 801ca1bcc4d..0e493aa6b79 100644 --- a/core/src/main/java/io/servicecomb/core/Const.java +++ b/core/src/main/java/io/servicecomb/core/Const.java @@ -39,4 +39,6 @@ private Const() { public static final String TARGET_MICROSERVICE = "x-cse-target-microservice"; public static final String REMOTE_ADDRESS = "x-cse-remote-address"; + + public static final String AUTH_TOKEN = "x-cse-auth-rsatoken"; } diff --git a/coverage-reports/pom.xml b/coverage-reports/pom.xml index 19f9815f2ed..15ba758b5a6 100644 --- a/coverage-reports/pom.xml +++ b/coverage-reports/pom.xml @@ -62,6 +62,10 @@ io.servicecomb handler-loadbalance + + io.servicecomb + handler-publickey-auth + io.servicecomb handler-flowcontrol-qps diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/RSAKeyPairEntry.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/RSAKeyPairEntry.java new file mode 100644 index 00000000000..5f152f19904 --- /dev/null +++ b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/RSAKeyPairEntry.java @@ -0,0 +1,47 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb.foundation.common.utils; + +import java.security.PrivateKey; +import java.security.PublicKey; + +public final class RSAKeyPairEntry { + + private PrivateKey privateKey; + + private PublicKey publicKey; + + private String publicKeyEncoded; + + public RSAKeyPairEntry(PrivateKey privateKey, PublicKey publicKey, String publicKeyEncoded) { + this.privateKey = privateKey; + this.publicKey = publicKey; + this.publicKeyEncoded = publicKeyEncoded; + } + + public PrivateKey getPrivateKey() { + return privateKey; + } + + public PublicKey getPublicKey() { + return publicKey; + } + + public String getPublicKeyEncoded() { + return publicKeyEncoded; + } + +} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/RSAUtils.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/RSAUtils.java new file mode 100644 index 00000000000..004e0122895 --- /dev/null +++ b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/common/utils/RSAUtils.java @@ -0,0 +1,113 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb.foundation.common.utils; + +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RSAUtils { + + private final static Logger LOGGER = LoggerFactory.getLogger(RSAUtils.class); + + private final static String RSA_ALG = "RSA"; + + private final static String SIGN_ALG = "SHA256withRSA"; + + private final static int KEY_SIZE = 2048; + + private static Base64.Encoder encoder = Base64.getEncoder(); + + private static Base64.Decoder decoder = Base64.getDecoder(); + + private static KeyFactory kf = null; + + static { + + try { + kf = KeyFactory.getInstance(RSA_ALG); + } catch (NoSuchAlgorithmException e) { + LOGGER.error("init keyfactory error"); + } + } + + public static RSAKeyPairEntry generateRSAKeyPair() { + try { + KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance(RSA_ALG); + keyGenerator.initialize(KEY_SIZE, new SecureRandom()); + KeyPair keyPair = keyGenerator.generateKeyPair(); + PublicKey pubKey = keyPair.getPublic(); + PrivateKey privKey = keyPair.getPrivate(); + return new RSAKeyPairEntry(privKey, pubKey, encoder.encodeToString(pubKey.getEncoded())); + } catch (NoSuchAlgorithmException e) { + LOGGER.error("generate rsa keypair faild"); + throw new IllegalStateException("perhaps error occurred on jre"); + } + } + + /** + * if has performance problem ,change Signature to ThreadLocal instance + */ + public static String sign(String content, PrivateKey privateKey) + throws NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, InvalidKeyException { + Signature signature = Signature.getInstance(SIGN_ALG); + signature.initSign(privateKey); + signature.update(content.getBytes()); + byte[] signByte = signature.sign(); + return encoder.encodeToString(signByte); + } + + /** + * + * if has performance problem ,change Signature to ThreadLocal instance + * @param publicKey public key after base64 encode + * @param sign 签名 + * @param content original content + * @return verify result + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + * @throws InvalidKeyException + * @throws SignatureException + */ + public static boolean verify(String publicKey, String sign, String content) + throws NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException, SignatureException { + if (null == kf ) + { + throw new NoSuchAlgorithmException(RSA_ALG + " KeyFactory not available"); + } + byte[] bytes = decoder.decode(publicKey); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes); + PublicKey pubKey = kf.generatePublic(keySpec); + Signature signature = Signature.getInstance(SIGN_ALG); + signature.initVerify(pubKey); + signature.update(content.getBytes()); + return signature.verify(decoder.decode(sign)); + } + +} diff --git a/foundations/foundation-common/src/main/java/io/servicecomb/foundation/token/RSAKeypair4Auth.java b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/token/RSAKeypair4Auth.java new file mode 100644 index 00000000000..84000e5ded0 --- /dev/null +++ b/foundations/foundation-common/src/main/java/io/servicecomb/foundation/token/RSAKeypair4Auth.java @@ -0,0 +1,52 @@ +package io.servicecomb.foundation.token; + +import java.security.PrivateKey; +import java.security.PublicKey; + +/** + * 进程级别公私钥对 + * + */ +public class RSAKeypair4Auth { + + private RSAKeypair4Auth() { + }; + + private PrivateKey privateKey; + + private PublicKey publicKey; + + private String publicKeyEncoded; + + + public PrivateKey getPrivateKey() { + return privateKey; + } + + + public void setPrivateKey(PrivateKey privateKey) { + this.privateKey = privateKey; + } + + + public PublicKey getPublicKey() { + return publicKey; + } + + + public void setPublicKey(PublicKey publicKey) { + this.publicKey = publicKey; + } + + + public String getPublicKeyEncoded() { + return publicKeyEncoded; + } + + + public void setPublicKeyEncoded(String publicKeyEncoded) { + this.publicKeyEncoded = publicKeyEncoded; + } + + public static RSAKeypair4Auth INSTANCE = new RSAKeypair4Auth(); +} diff --git a/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestRSAUtil.java b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestRSAUtil.java new file mode 100644 index 00000000000..72a27d6d236 --- /dev/null +++ b/foundations/foundation-common/src/test/java/io/servicecomb/foundation/common/utils/TestRSAUtil.java @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb.foundation.common.utils; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; + +import org.junit.Assert; +import org.junit.Test; + +public class TestRSAUtil { + + @Test + public void testSignVerify() + throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException { + RSAKeyPairEntry rsaKeyPairEntry = RSAUtils.generateRSAKeyPair(); + + Assert.assertNotNull(rsaKeyPairEntry.getPublicKeyEncoded()); + Assert.assertNotNull(rsaKeyPairEntry.getPrivateKey()); + Assert.assertNotNull(rsaKeyPairEntry.getPublicKey()); + String testContent = "instance-id@201711201930@randomstr"; + String signstr = RSAUtils.sign(testContent, rsaKeyPairEntry.getPrivateKey()); + Assert.assertTrue(RSAUtils.verify(rsaKeyPairEntry.getPublicKeyEncoded(), signstr, testContent)); + + } + + @Test + public void testSignVerify2() + throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException { + String sign = + "WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; + String content = + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ"; + String pubKey = + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxKl5TNUTec7fL2degQcCk6vKf3c0wsfNK5V6elKzjWxm0MwbRj/UeR20VSnicBmVIOWrBS9LiERPPvjmmWUOSS2vxwr5XfhBhZ07gCAUNxBOTzgMo5nE45DhhZu5Jzt5qSV6o10Kq7+fCCBlDZ1UoWxZceHkUt5AxcrhEDulFjQIDAQAB"; + Assert.assertTrue(RSAUtils.verify(pubKey, sign, content)); + + } + +} diff --git a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/TestMetricsConfig.java b/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/TestMetricsConfig.java index afef3585322..7d462883eca 100644 --- a/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/TestMetricsConfig.java +++ b/foundations/foundation-metrics/src/test/java/io/servicecomb/foundation/metrics/TestMetricsConfig.java @@ -24,6 +24,6 @@ public class TestMetricsConfig { @Test public void test() { Assert.assertEquals(60000, MetricsConfig.getMsCycle()); - Assert.assertEquals( false, MetricsConfig.isEnable()); + Assert.assertEquals(false, MetricsConfig.isEnable()); } } diff --git a/handlers/handler-publickey-auth/pom.xml b/handlers/handler-publickey-auth/pom.xml new file mode 100644 index 00000000000..2ffb8f692d5 --- /dev/null +++ b/handlers/handler-publickey-auth/pom.xml @@ -0,0 +1,50 @@ + + + + 4.0.0 + + io.servicecomb + handlers + 0.4.1-SNAPSHOT + + + handler-publickey-auth + + + UTF-8 + + + + + io.servicecomb + java-chassis-core + + + org.slf4j + slf4j-log4j12 + test + + + log4j + log4j + test + + + + + diff --git a/handlers/handler-publickey-auth/src/main/java/io/servicecomb/AuthHandlerBoot.java b/handlers/handler-publickey-auth/src/main/java/io/servicecomb/AuthHandlerBoot.java new file mode 100644 index 00000000000..f0ccc547190 --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/io/servicecomb/AuthHandlerBoot.java @@ -0,0 +1,51 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb; + +import io.servicecomb.core.BootListener; +import io.servicecomb.foundation.common.utils.RSAKeyPairEntry; +import io.servicecomb.foundation.common.utils.RSAUtils; +import io.servicecomb.foundation.token.RSAKeypair4Auth; +import io.servicecomb.serviceregistry.RegistryUtils; +import io.servicecomb.serviceregistry.api.Const; + +import org.springframework.stereotype.Component; + +/** + * + * initialize public and private key pair when system boot before registry instance to service center + * + * + */ +@Component +public class AuthHandlerBoot implements BootListener { + + + @Override + public void onBootEvent(BootEvent event) { + if (EventType.BEFORE_REGISTRY.equals(event.getEventType())) { + RSAKeyPairEntry rsaKeyPairEntry = RSAUtils.generateRSAKeyPair(); + RSAKeypair4Auth.INSTANCE.setPrivateKey(rsaKeyPairEntry.getPrivateKey()); + RSAKeypair4Auth.INSTANCE.setPublicKey(rsaKeyPairEntry.getPublicKey()); + RSAKeypair4Auth.INSTANCE.setPublicKeyEncoded(rsaKeyPairEntry.getPublicKeyEncoded()); + RegistryUtils.getMicroserviceInstance().getProperties().put(Const.INSTANCE_PUBKEY_PRO, rsaKeyPairEntry.getPublicKeyEncoded()); + } + + } + + + +} diff --git a/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/RSAAuthenticationToken.java b/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/RSAAuthenticationToken.java new file mode 100644 index 00000000000..89a849c0320 --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/RSAAuthenticationToken.java @@ -0,0 +1,116 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb.authentication; + + +/** + * token 组成部分: + * token: instanceId@@generateTime@randomCode@sign(instanceId@@generateTime@randomCode) + * + */ +public class RSAAuthenticationToken { + + public final static long TOKEN_ACTIVE_TIME = 24 * 60 * 60 * 1000; + + private String instanceId; + + private String serviceId; + + private long generateTime; + + private String randomCode; + + private String sign; + + private String tokenFormat; + + public RSAAuthenticationToken(String instanceId, String serviceId, long generateTime, + String randomCode, String sign) { + this.instanceId = instanceId; + this.generateTime = generateTime; + this.randomCode = randomCode; + this.serviceId = serviceId; + this.sign = sign; + this.tokenFormat = String.format("%s@%s@%s@%s@%s", + instanceId, + serviceId, + generateTime, + randomCode, + sign); + } + + public String plainToken() { + return String.format("%s@%s@%s@%s", this.instanceId, this.serviceId, this.generateTime, this.randomCode); + } + + + public String getInstanceId() { + return instanceId; + } + + + public long getGenerateTime() { + return generateTime; + } + + + public String getSign() { + return sign; + } + + + public String format() { + return tokenFormat; + } + + public static RSAAuthenticationToken fromStr(String token) { + String[] tokenArr = token.split("@"); + if (tokenArr.length != 5) { + return null; + } + return new RSAAuthenticationToken(tokenArr[0], tokenArr[1], + Long.valueOf(tokenArr[2]), tokenArr[3], tokenArr[4]); + } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof RSAAuthenticationToken)) { + return false; + } + RSAAuthenticationToken token = (RSAAuthenticationToken) obj; + if (!token.plainToken().equals(this.plainToken())) { + return false; + } + if (!token.getSign().equals(this.sign)) { + return false; + } + return true; + } + + public int hashCode() { + return this.plainToken().hashCode() + this.sign.hashCode(); + } + + +} diff --git a/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/consumer/ConsumerAuthHandler.java b/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/consumer/ConsumerAuthHandler.java new file mode 100644 index 00000000000..148143fcb71 --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/consumer/ConsumerAuthHandler.java @@ -0,0 +1,53 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb.authentication.consumer; + +import java.util.Optional; + +import io.servicecomb.core.Const; +import io.servicecomb.core.Handler; +import io.servicecomb.core.Invocation; +import io.servicecomb.swagger.invocation.AsyncResponse; + +/** + * + * add token to context + * Provider will get token for authentication + * + */ +public class ConsumerAuthHandler implements Handler { + + private RSAConsumerTokenManager athenticationTokenManager = new RSAConsumerTokenManager(); + + @Override + public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception { + + Optional token = Optional.ofNullable(athenticationTokenManager.getToken()); + if(!token.isPresent()) + { + asyncResp.consumerFail( + new IllegalStateException("rejected by consumer authentication handler")); + return ; + } + invocation.addContext(Const.AUTH_TOKEN, token.get()); + invocation.next(asyncResp); + } + + public void setAuthenticationTokenManager(RSAConsumerTokenManager authenticationTokenManager) { + this.athenticationTokenManager = authenticationTokenManager; + } + +} diff --git a/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/consumer/RSAConsumerTokenManager.java b/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/consumer/RSAConsumerTokenManager.java new file mode 100644 index 00000000000..6b7328af0cd --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/consumer/RSAConsumerTokenManager.java @@ -0,0 +1,96 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb.authentication.consumer; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.apache.commons.lang3.RandomStringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.servicecomb.authentication.RSAAuthenticationToken; +import io.servicecomb.foundation.common.utils.RSAUtils; +import io.servicecomb.foundation.token.RSAKeypair4Auth; +import io.servicecomb.serviceregistry.RegistryUtils; + +public class RSAConsumerTokenManager { + + private static final Logger logger = LoggerFactory.getLogger(RSAConsumerTokenManager.class); + + private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + + private RSAAuthenticationToken token; + + public String getToken() { + readWriteLock.readLock().lock(); + if (!isExpired(token)) { + String tokenStr = token.format(); + readWriteLock.readLock().unlock(); + return tokenStr; + } else { + readWriteLock.readLock().unlock(); + return createToken(); + } + } + + public String createToken() { + PrivateKey privateKey = RSAKeypair4Auth.INSTANCE.getPrivateKey(); + readWriteLock.writeLock().lock(); + if (!isExpired(token)) { + logger.debug("Token had been recreated by another thread"); + readWriteLock.writeLock().unlock(); + return token.format(); + } + String instanceId = RegistryUtils.getMicroserviceInstance().getInstanceId(); + String serviceId = RegistryUtils.getMicroservice().getServiceId(); + String randomCode = RandomStringUtils.randomAlphanumeric(128); + long generateTime = System.currentTimeMillis(); + try { + String plain = String.format("%s@%s@%s@%s", instanceId, serviceId, generateTime, randomCode); + String sign = RSAUtils.sign(plain, privateKey); + token = RSAAuthenticationToken.fromStr(String.format("%s@%s", plain, sign)); + return token.format(); + } catch (InvalidKeyException | NoSuchAlgorithmException | InvalidKeySpecException | SignatureException e) { + logger.error("create token error", e); + throw new Error("create token error"); + } finally { + readWriteLock.writeLock().unlock(); + } + + } + + /** + * the TTL of Token is 24 hours + * client token will expired 15 minutes early + */ + public boolean isExpired(RSAAuthenticationToken token) { + if (null == token) { + return true; + } + long generateTime = token.getGenerateTime(); + long expiredDate = generateTime + RSAAuthenticationToken.TOKEN_ACTIVE_TIME - 15 * 60 * 1000; + long now = System.currentTimeMillis(); + return now > expiredDate; + } + + +} diff --git a/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/provider/ProviderAuthHanlder.java b/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/provider/ProviderAuthHanlder.java new file mode 100644 index 00000000000..068002b9faa --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/provider/ProviderAuthHanlder.java @@ -0,0 +1,41 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb.authentication.provider; + +import io.servicecomb.core.Const; +import io.servicecomb.core.Handler; +import io.servicecomb.core.Invocation; +import io.servicecomb.swagger.invocation.AsyncResponse; +import io.servicecomb.swagger.invocation.context.HttpStatus; +import io.servicecomb.swagger.invocation.exception.InvocationException; + +public class ProviderAuthHanlder implements Handler { + + private RSAProviderTokenManager authenticationTokenManager = new RSAProviderTokenManager(); + + @Override + public void handle(Invocation invocation, AsyncResponse asyncResp) throws Exception { + + String token = invocation.getContext(Const.AUTH_TOKEN); + if (null != token && authenticationTokenManager.valid(token)) { + invocation.next(asyncResp); + } else { + asyncResp.producerFail(new InvocationException(new HttpStatus(401, "UNAUTHORIZED"), "UNAUTHORIZED")); + } + + } + +} diff --git a/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/provider/RSAProviderTokenManager.java b/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/provider/RSAProviderTokenManager.java new file mode 100644 index 00000000000..d7826e8fe68 --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/io/servicecomb/authentication/provider/RSAProviderTokenManager.java @@ -0,0 +1,91 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb.authentication.provider; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.servicecomb.authentication.RSAAuthenticationToken; +import io.servicecomb.foundation.common.utils.RSAUtils; +import io.servicecomb.serviceregistry.api.Const; +import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; +import io.servicecomb.serviceregistry.cache.MicroserviceInstanceCache; + +public class RSAProviderTokenManager { + + private final static Logger LOGGER = LoggerFactory.getLogger(RSAProviderTokenManager.class); + + private Set validatedToken = ConcurrentHashMap.newKeySet(1000); + + public boolean valid(String token) { + try { + RSAAuthenticationToken rsaToken = RSAAuthenticationToken.fromStr(token); + if (null == rsaToken) { + LOGGER.error("token format is error, perhaps you need to set auth handler at consumer"); + return false; + } + if (tokenExprired(rsaToken)) { + LOGGER.error("token is expired"); + return false; + } + if (validatedToken.contains(rsaToken)) { + LOGGER.info("found vaildate token in vaildate pool"); + return true; + } + + String sign = rsaToken.getSign(); + String content = rsaToken.plainToken(); + String publicKey = getPublicKey(rsaToken.getInstanceId(), rsaToken.getServiceId()); + boolean verify = RSAUtils.verify(publicKey, sign, content); + if (verify && !tokenExprired(rsaToken)) { + validatedToken.add(rsaToken); + return true; + } + + LOGGER.error("token verify error"); + return false; + + } catch (InvalidKeyException | NoSuchAlgorithmException | InvalidKeySpecException | SignatureException e) { + LOGGER.error("verfiy error", e); + return false; + } + } + + private boolean tokenExprired(RSAAuthenticationToken rsaToken) { + long generateTime = rsaToken.getGenerateTime(); + long expired = generateTime + RSAAuthenticationToken.TOKEN_ACTIVE_TIME + 15 * 60 * 1000; + long now = System.currentTimeMillis(); + return now > expired; + } + + private String getPublicKey(String instanceId, String serviceId) { + MicroserviceInstance instances = MicroserviceInstanceCache.getOrCreate(serviceId, instanceId); + if (instances != null) { + return instances.getProperties().get(Const.INSTANCE_PUBKEY_PRO); + } else { + LOGGER.error("not instance found {}-{}, maybe attack", instanceId, serviceId); + return ""; + } + } + +} diff --git a/handlers/handler-publickey-auth/src/main/resources/config/cse.handler.xml b/handlers/handler-publickey-auth/src/main/resources/config/cse.handler.xml new file mode 100644 index 00000000000..8b3d1a39251 --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/resources/config/cse.handler.xml @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestAuthHandlerBoot.java b/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestAuthHandlerBoot.java new file mode 100644 index 00000000000..047885b58ec --- /dev/null +++ b/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestAuthHandlerBoot.java @@ -0,0 +1,53 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb.authentication; + +import io.servicecomb.AuthHandlerBoot; +import io.servicecomb.core.BootListener; +import io.servicecomb.core.BootListener.BootEvent; +import io.servicecomb.foundation.token.RSAKeypair4Auth; +import io.servicecomb.serviceregistry.RegistryUtils; +import io.servicecomb.serviceregistry.api.registry.Microservice; +import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; +import mockit.Expectations; + +import org.junit.Assert; +import org.junit.Test; + +public class TestAuthHandlerBoot { + + + @Test + public void testGenerateRSAKey() { + MicroserviceInstance microserviceInstance = new MicroserviceInstance(); + Microservice microservice = new Microservice(); + microservice.setIntance(microserviceInstance); + new Expectations(RegistryUtils.class) { + { + + RegistryUtils.getMicroserviceInstance(); + result = microserviceInstance; + } + }; + + AuthHandlerBoot authHandlerBoot = new AuthHandlerBoot(); + BootEvent bootEvent = new BootEvent(); + bootEvent.setEventType(BootListener.EventType.BEFORE_REGISTRY); + authHandlerBoot.onBootEvent(bootEvent); + Assert.assertNotNull(RSAKeypair4Auth.INSTANCE.getPrivateKey()); + Assert.assertNotNull(RSAKeypair4Auth.INSTANCE.getPublicKey()); + } +} diff --git a/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestConsumerAuthHandler.java b/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestConsumerAuthHandler.java new file mode 100644 index 00000000000..42f01b03722 --- /dev/null +++ b/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestConsumerAuthHandler.java @@ -0,0 +1,64 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb.authentication; + +import io.servicecomb.authentication.consumer.ConsumerAuthHandler; +import io.servicecomb.authentication.consumer.RSAConsumerTokenManager; +import io.servicecomb.core.Invocation; +import io.servicecomb.swagger.invocation.AsyncResponse; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class TestConsumerAuthHandler { + + Invocation invocation = null; + + AsyncResponse asyncResp = null; + + RSAConsumerTokenManager tokenManager = null; + + @Test + public void testHandler() throws Exception { + tokenManager = Mockito.mock(RSAConsumerTokenManager.class); + Mockito.when(tokenManager.getToken()).thenReturn("testtoken"); + ConsumerAuthHandler consumerAuthHandler = new ConsumerAuthHandler(); + consumerAuthHandler.setAuthenticationTokenManager(tokenManager); + consumerAuthHandler.handle(invocation, asyncResp); + Assert.assertTrue(true); + } + + + @Test + public void testHandlerException() throws Exception { + tokenManager = Mockito.mock(RSAConsumerTokenManager.class); + Mockito.when(tokenManager.getToken()).thenReturn(null); + ConsumerAuthHandler consumerAuthHandler = new ConsumerAuthHandler(); + consumerAuthHandler.setAuthenticationTokenManager(tokenManager); + consumerAuthHandler.handle(invocation, asyncResp); + + } + + @Before + public void setUp() throws Exception { + invocation = Mockito.mock(Invocation.class); + asyncResp = Mockito.mock(AsyncResponse.class); + + + } +} diff --git a/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestProviderAuthHanlder.java b/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestProviderAuthHanlder.java new file mode 100644 index 00000000000..80bf5555157 --- /dev/null +++ b/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestProviderAuthHanlder.java @@ -0,0 +1,46 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb.authentication; + +import io.servicecomb.authentication.provider.ProviderAuthHanlder; +import io.servicecomb.core.Const; +import io.servicecomb.core.Invocation; +import io.servicecomb.swagger.invocation.AsyncResponse; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class TestProviderAuthHanlder { + Invocation invocation = null; + + AsyncResponse asyncResp = null; + + @Before + public void setUp() throws Exception { + invocation = Mockito.mock(Invocation.class); + asyncResp = Mockito.mock(AsyncResponse.class); + Mockito.when(invocation.getContext(Const.AUTH_TOKEN)).thenReturn("testtoken"); + } + + @Test + public void testHandle() throws Exception { + ProviderAuthHanlder providerAuthHanlder = new ProviderAuthHanlder(); + providerAuthHanlder.handle(invocation, asyncResp); + Assert.assertTrue(true); + } +} diff --git a/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestRSAAuthenticationToken.java b/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestRSAAuthenticationToken.java new file mode 100644 index 00000000000..b50fc824682 --- /dev/null +++ b/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestRSAAuthenticationToken.java @@ -0,0 +1,79 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb.authentication; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SignatureException; +import java.security.spec.InvalidKeySpecException; + +import org.junit.Assert; +import org.junit.Test; + +import io.servicecomb.foundation.common.utils.RSAUtils; + + +public class TestRSAAuthenticationToken { + + + @Test + public void testRSAAuthenticationToken() + throws InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException { + String tokenstr = + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; + RSAAuthenticationToken token = RSAAuthenticationToken.fromStr(tokenstr); + String contents = token.plainToken(); + Assert.assertEquals( + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ", + contents); + String sign = token.getSign(); + Assert.assertEquals( + "WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk=", + sign); + String pubKey = + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxKl5TNUTec7fL2degQcCk6vKf3c0wsfNK5V6elKzjWxm0MwbRj/UeR20VSnicBmVIOWrBS9LiERPPvjmmWUOSS2vxwr5XfhBhZ07gCAUNxBOTzgMo5nE45DhhZu5Jzt5qSV6o10Kq7+fCCBlDZ1UoWxZceHkUt5AxcrhEDulFjQIDAQAB"; + Assert.assertTrue(RSAUtils.verify(pubKey, sign, contents)); + } + + + @Test + public void testRSAAuthenticationTokenError() { + String tokenstr = + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; + RSAAuthenticationToken token = RSAAuthenticationToken.fromStr(tokenstr); + Assert.assertNull(token); + } + + + @Test + public void testEqual() { + String tokenstr = + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; + RSAAuthenticationToken token = RSAAuthenticationToken.fromStr(tokenstr); + Assert.assertNotEquals(token, null); + RSAAuthenticationToken token2 = RSAAuthenticationToken.fromStr( + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk"); + Assert.assertNotEquals(token2, token); + + RSAAuthenticationToken token3 = RSAAuthenticationToken.fromStr( + "e8a0a4b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk"); + Assert.assertNotEquals(token3, token); + + RSAAuthenticationToken token4 = RSAAuthenticationToken.fromStr( + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="); + Assert.assertEquals(token4, token); + } +} diff --git a/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestRSAProviderTokenManager.java b/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestRSAProviderTokenManager.java new file mode 100644 index 00000000000..17139b62ad1 --- /dev/null +++ b/handlers/handler-publickey-auth/src/test/java/io/servicecomb/authentication/TestRSAProviderTokenManager.java @@ -0,0 +1,98 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb.authentication; + +import io.servicecomb.authentication.consumer.RSAConsumerTokenManager; +import io.servicecomb.authentication.provider.RSAProviderTokenManager; +import io.servicecomb.foundation.common.utils.RSAKeyPairEntry; +import io.servicecomb.foundation.common.utils.RSAUtils; +import io.servicecomb.foundation.token.RSAKeypair4Auth; +import io.servicecomb.serviceregistry.RegistryUtils; +import io.servicecomb.serviceregistry.api.Const; +import io.servicecomb.serviceregistry.api.registry.Microservice; +import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; +import io.servicecomb.serviceregistry.cache.MicroserviceInstanceCache; + +import java.util.HashMap; +import java.util.Map; + +import mockit.Expectations; + +import org.junit.Assert; +import org.junit.Test; + +public class TestRSAProviderTokenManager { + + + @Test + public void testTokenExpried() { + String tokenStr = + "e8a04b54cf2711e7b701286ed488fc20@c8636e5acf1f11e7b701286ed488fc20@1511315597475@9t0tp8ce80SUM5ts6iRGjFJMvCdQ7uvhpyh0RM7smKm3p4wYOrojr4oT1Pnwx7xwgcgEFbQdwPJxIMfivpQ1rHGqiLp67cjACvJ3Ke39pmeAVhybsLADfid6oSjscFaJ@WBYouF6hXYrXzBA31HC3VX8Bw9PNgJUtVqOPAaeW9ye3q/D7WWb0M+XMouBIWxWY6v9Un1dGu5Rkjlx6gZbnlHkb2VO8qFR3Y6lppooWCirzpvEBRjlJQu8LPBur0BCfYGq8XYrEZA2NU6sg2zXieqCSiX6BnMnBHNn4cR9iZpk="; + RSAProviderTokenManager tokenManager = new RSAProviderTokenManager(); + MicroserviceInstance microserviceInstance = new MicroserviceInstance(); + Map properties = new HashMap(); + microserviceInstance.setProperties(properties); + properties.put(Const.INSTANCE_PUBKEY_PRO, + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxKl5TNUTec7fL2degQcCk6vKf3c0wsfNK5V6elKzjWxm0MwbRj/UeR20VSnicBmVIOWrBS9LiERPPvjmmWUOSS2vxwr5XfhBhZ07gCAUNxBOTzgMo5nE45DhhZu5Jzt5qSV6o10Kq7+fCCBlDZ1UoWxZceHkUt5AxcrhEDulFjQIDAQAB"); + Assert.assertFalse(tokenManager.valid(tokenStr)); + } + + @Test + public void testTokenFromVaidatePool() { + RSAKeyPairEntry rsaKeyPairEntry = RSAUtils.generateRSAKeyPair(); + RSAKeypair4Auth.INSTANCE.setPrivateKey(rsaKeyPairEntry.getPrivateKey()); + RSAKeypair4Auth.INSTANCE.setPublicKey(rsaKeyPairEntry.getPublicKey()); + RSAKeypair4Auth.INSTANCE.setPublicKeyEncoded(rsaKeyPairEntry.getPublicKeyEncoded()); + String serviceId = "c8636e5acf1f11e7b701286ed488fc20"; + String instanceId = "e8a04b54cf2711e7b701286ed488fc20"; + RSAConsumerTokenManager rsaCoumserTokenManager = new RSAConsumerTokenManager(); + MicroserviceInstance microserviceInstance = new MicroserviceInstance(); + microserviceInstance.setInstanceId(instanceId); + Map properties = new HashMap(); + microserviceInstance.setProperties(properties); + properties.put(Const.INSTANCE_PUBKEY_PRO, rsaKeyPairEntry.getPublicKeyEncoded()); + Microservice microservice = new Microservice(); + microservice.setServiceId(serviceId); + new Expectations(RegistryUtils.class) { + { + RegistryUtils.getMicroservice(); + result = microservice; + RegistryUtils.getMicroserviceInstance(); + result = microserviceInstance; + + } + }; + + //Test Consumer first create token + String token = rsaCoumserTokenManager.getToken(); + Assert.assertNotNull(token); + // use cache token + Assert.assertEquals(token, rsaCoumserTokenManager.getToken()); + new Expectations(MicroserviceInstanceCache.class) { + { + MicroserviceInstanceCache.getOrCreate(serviceId, instanceId); + result = microserviceInstance; + + } + }; + RSAProviderTokenManager rsaProviderTokenManager = new RSAProviderTokenManager(); + //first validate need to verify use RSA + Assert.assertTrue(rsaProviderTokenManager.valid(token)); + // second validate use validated pool + Assert.assertTrue(rsaProviderTokenManager.valid(token)); + } + +} diff --git a/handlers/handler-publickey-auth/src/test/resources/log4j.properties b/handlers/handler-publickey-auth/src/test/resources/log4j.properties new file mode 100644 index 00000000000..82ef8a6612c --- /dev/null +++ b/handlers/handler-publickey-auth/src/test/resources/log4j.properties @@ -0,0 +1,29 @@ +# +# Copyright 2017 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +log4j.rootLogger=INFO, out, stdout + +# CONSOLE appender not used by default +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n + +# File appender +log4j.appender.out=org.apache.log4j.FileAppender +log4j.appender.out.layout=org.apache.log4j.PatternLayout +log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n +log4j.appender.out.file=target/test.log +log4j.appender.out.append=true diff --git a/handlers/pom.xml b/handlers/pom.xml index 071e6405e60..ad6913c2ca5 100644 --- a/handlers/pom.xml +++ b/handlers/pom.xml @@ -34,6 +34,7 @@ handler-bizkeeper handler-flowcontrol-qps handler-loadbalance + handler-publickey-auth diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index c5e8dff11b0..75271a624e4 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -1,7 +1,5 @@ - + io.servicecomb java-chassis-parent @@ -100,4 +98,4 @@ - + \ No newline at end of file diff --git a/java-chassis-dependencies/pom.xml b/java-chassis-dependencies/pom.xml index 7683c6a41bb..cabfac75f09 100644 --- a/java-chassis-dependencies/pom.xml +++ b/java-chassis-dependencies/pom.xml @@ -764,6 +764,11 @@ io.servicecomb handler-flowcontrol-qps 0.4.1-SNAPSHOT + + + io.servicecomb + handler-publickey-auth + 0.4.1-SNAPSHOT io.servicecomb diff --git a/samples/auth-sample/auth-consumer/pom.xml b/samples/auth-sample/auth-consumer/pom.xml new file mode 100644 index 00000000000..7f7bedf1890 --- /dev/null +++ b/samples/auth-sample/auth-consumer/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + io.servicecomb.samples + auth-sample + 0.4.1-SNAPSHOT + + auth-consumer + + + io.servicecomb + transport-highway + + + io.servicecomb + transport-rest-vertx + + + io.servicecomb + handler-loadbalance + + + io.servicecomb + provider-springmvc + + + io.servicecomb + provider-pojo + + + io.servicecomb + handler-publickey-auth + + + org.slf4j + slf4j-log4j12 + + + io.servicecomb.samples + commmon-schema + + + \ No newline at end of file diff --git a/samples/auth-sample/auth-consumer/src/main/java/io/servicecomb/samples/springmvc/consumer/AuthConsumerMain.java b/samples/auth-sample/auth-consumer/src/main/java/io/servicecomb/samples/springmvc/consumer/AuthConsumerMain.java new file mode 100644 index 00000000000..8f348904b15 --- /dev/null +++ b/samples/auth-sample/auth-consumer/src/main/java/io/servicecomb/samples/springmvc/consumer/AuthConsumerMain.java @@ -0,0 +1,55 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.servicecomb.samples.springmvc.consumer; + +import io.servicecomb.foundation.common.utils.BeanUtils; +import io.servicecomb.foundation.common.utils.Log4jUtils; +import io.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import io.servicecomb.samples.common.schema.models.Person; + +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import org.springframework.web.client.RestTemplate; + +@Component +public class AuthConsumerMain { + + private static RestTemplate restTemplate = RestTemplateBuilder.create(); + + public static void main(String[] args) throws Exception { + init(); + Person person = new Person(); + person.setName("ServiceComb/Authenticate"); + System.out + .println("RestTemplate Consumer or POJO Consumer. You can choose whatever you like."); + String sayHiResult = restTemplate + .postForObject( + "cse://auth-provider/springmvchello/sayhi?name=Authenticate", + null, + String.class); + String sayHelloResult = restTemplate.postForObject( + "cse://auth-provider/springmvchello/sayhello", + person, + String.class); + Assert.isTrue("Hello Authenticate".equals(sayHiResult)); + Assert.isTrue("Hello person ServiceComb/Authenticate".equals(sayHelloResult)); + } + + public static void init() throws Exception { + Log4jUtils.init(); + BeanUtils.init(); + } +} diff --git a/samples/auth-sample/auth-consumer/src/main/resources/config/log4j.properties b/samples/auth-sample/auth-consumer/src/main/resources/config/log4j.properties new file mode 100644 index 00000000000..0324fbb1032 --- /dev/null +++ b/samples/auth-sample/auth-consumer/src/main/resources/config/log4j.properties @@ -0,0 +1,20 @@ +# +# Copyright 2017 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +paas.logs.dir=target/logs/ +paas.logs.file=sample.log + +log4j.rootLogger=INFO,paas,stdout diff --git a/samples/auth-sample/auth-consumer/src/main/resources/microservice.yaml b/samples/auth-sample/auth-consumer/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..1956f054e06 --- /dev/null +++ b/samples/auth-sample/auth-consumer/src/main/resources/microservice.yaml @@ -0,0 +1,15 @@ +APPLICATION_ID: auth-sample +service_description: + name: authConsumer + version: 0.0.1 +cse: + service: + registry: + address: http://127.0.0.1:30100 + handler: + chain: + Consumer: + default: auth-consumer, loadbalance + references: + auth-provider: + version-rule: 0.0.1 diff --git a/samples/auth-sample/auth-provider/pom.xml b/samples/auth-sample/auth-provider/pom.xml new file mode 100644 index 00000000000..713850339f1 --- /dev/null +++ b/samples/auth-sample/auth-provider/pom.xml @@ -0,0 +1,52 @@ + + + + 4.0.0 + + io.servicecomb.samples + auth-sample + 0.4.1-SNAPSHOT + + auth-provider + + + io.servicecomb + transport-highway + + + io.servicecomb + transport-rest-vertx + + + io.servicecomb + handler-publickey-auth + + + io.servicecomb + provider-springmvc + + + org.slf4j + slf4j-log4j12 + + + io.servicecomb.samples + commmon-schema + + + \ No newline at end of file diff --git a/samples/auth-sample/auth-provider/src/main/java/io/servicecomb/samples/springmvc/provider/AuthProviderMain.java b/samples/auth-sample/auth-provider/src/main/java/io/servicecomb/samples/springmvc/provider/AuthProviderMain.java new file mode 100644 index 00000000000..1929037a36c --- /dev/null +++ b/samples/auth-sample/auth-provider/src/main/java/io/servicecomb/samples/springmvc/provider/AuthProviderMain.java @@ -0,0 +1,28 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.servicecomb.samples.springmvc.provider; + +import io.servicecomb.foundation.common.utils.BeanUtils; +import io.servicecomb.foundation.common.utils.Log4jUtils; + +public class AuthProviderMain { + + public static void main(String[] args) throws Exception { + Log4jUtils.init(); + BeanUtils.init(); + } +} diff --git a/samples/auth-sample/auth-provider/src/main/java/io/servicecomb/samples/springmvc/provider/SpringmvcHelloImpl.java b/samples/auth-sample/auth-provider/src/main/java/io/servicecomb/samples/springmvc/provider/SpringmvcHelloImpl.java new file mode 100644 index 00000000000..46a9ce5689b --- /dev/null +++ b/samples/auth-sample/auth-provider/src/main/java/io/servicecomb/samples/springmvc/provider/SpringmvcHelloImpl.java @@ -0,0 +1,46 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.servicecomb.samples.springmvc.provider; + + +import io.servicecomb.provider.rest.common.RestSchema; +import io.servicecomb.samples.common.schema.Hello; +import io.servicecomb.samples.common.schema.models.Person; + +import javax.ws.rs.core.MediaType; + +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +@RestSchema(schemaId = "springmvcHello") +@RequestMapping(path = "/springmvchello", produces = MediaType.APPLICATION_JSON) +public class SpringmvcHelloImpl implements Hello { + + @Override + @RequestMapping(path = "/sayhi", method = RequestMethod.POST) + public String sayHi(@RequestParam(name = "name") String name) { + return "Hello " + name; + } + + @Override + @RequestMapping(path = "/sayhello", method = RequestMethod.POST) + public String sayHello(@RequestBody Person person) { + return "Hello person " + person.getName(); + } +} diff --git a/samples/auth-sample/auth-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml b/samples/auth-sample/auth-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml new file mode 100644 index 00000000000..2acd82877ea --- /dev/null +++ b/samples/auth-sample/auth-provider/src/main/resources/META-INF/spring/pojo.provider.bean.xml @@ -0,0 +1,28 @@ + + + + + + + \ No newline at end of file diff --git a/samples/auth-sample/auth-provider/src/main/resources/config/log4j.properties b/samples/auth-sample/auth-provider/src/main/resources/config/log4j.properties new file mode 100644 index 00000000000..0324fbb1032 --- /dev/null +++ b/samples/auth-sample/auth-provider/src/main/resources/config/log4j.properties @@ -0,0 +1,20 @@ +# +# Copyright 2017 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +paas.logs.dir=target/logs/ +paas.logs.file=sample.log + +log4j.rootLogger=INFO,paas,stdout diff --git a/samples/auth-sample/auth-provider/src/main/resources/microservice.yaml b/samples/auth-sample/auth-provider/src/main/resources/microservice.yaml new file mode 100644 index 00000000000..1b7856729a0 --- /dev/null +++ b/samples/auth-sample/auth-provider/src/main/resources/microservice.yaml @@ -0,0 +1,16 @@ +APPLICATION_ID: auth-sample +service_description: + name: auth-provider + version: 0.0.1 +cse: + service: + registry: + address: http://127.0.0.1:30100 + handler: + chain: + Provider: + default: auth-provider + rest: + address: 0.0.0.0:8082 + highway: + address: 0.0.0.0:7070 diff --git a/samples/auth-sample/pom.xml b/samples/auth-sample/pom.xml new file mode 100644 index 00000000000..f978e24278b --- /dev/null +++ b/samples/auth-sample/pom.xml @@ -0,0 +1,14 @@ + + 4.0.0 + + io.servicecomb.samples + samples + 0.4.1-SNAPSHOT + + auth-sample + pom + + auth-provider + auth-consumer + + \ No newline at end of file diff --git a/samples/pom.xml b/samples/pom.xml index 9ec04d71035..772404f960a 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -30,7 +30,10 @@ pojo-sample springmvc-sample commmon-schema + auth-sample bmi + dubbo-to-servicecomb-sample + diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/Const.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/Const.java index 88668697e5f..6081115cd8e 100644 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/Const.java +++ b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/Const.java @@ -147,4 +147,6 @@ public static final class REGISTRY_API { public static final String PATH_CHECKSESSION = "checksession"; public static final String URL_PREFIX = "urlPrefix"; + + public static final String INSTANCE_PUBKEY_PRO = "publickey"; } diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/MicroserviceInstanceResponse.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/MicroserviceInstanceResponse.java new file mode 100644 index 00000000000..76f801b409e --- /dev/null +++ b/service-registry/src/main/java/io/servicecomb/serviceregistry/api/response/MicroserviceInstanceResponse.java @@ -0,0 +1,34 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.servicecomb.serviceregistry.api.response; + +import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; + +public class MicroserviceInstanceResponse { + + private MicroserviceInstance instance; + + public MicroserviceInstance getInstance() { + return instance; + } + + public void setInstance(MicroserviceInstance instance) { + this.instance = instance; + } + + +} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/InstanceCacheManager.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/InstanceCacheManager.java index 38d6828dce1..38b127f2c7b 100644 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/InstanceCacheManager.java +++ b/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/InstanceCacheManager.java @@ -22,4 +22,5 @@ public interface InstanceCacheManager { InstanceCache getOrCreate(String appId, String microserviceName, String microserviceVersionRule); VersionedCache getOrCreateVersionedCache(String appId, String microserviceName, String microserviceVersionRule); + } diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/MicroserviceInstanceCache.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/MicroserviceInstanceCache.java new file mode 100644 index 00000000000..1f0aca05f6b --- /dev/null +++ b/service-registry/src/main/java/io/servicecomb/serviceregistry/cache/MicroserviceInstanceCache.java @@ -0,0 +1,67 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.servicecomb.serviceregistry.cache; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +import io.servicecomb.serviceregistry.RegistryUtils; +import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; + +/** + * 微服务实例缓存 key为:serviceId@instanceId 缓存limit:1000 缓存老化策略:30分钟没有访问就过期。 + * + */ +public class MicroserviceInstanceCache { + + private static final Logger logger = LoggerFactory.getLogger(MicroserviceInstanceCache.class); + + private static Cache instances = CacheBuilder.newBuilder() + .maximumSize(1000) + .expireAfterAccess(30, TimeUnit.MINUTES) + .build(); + + public static MicroserviceInstance getOrCreate(String serviceId, String instanceId) { + try { + String key = String.format("%s@%s", serviceId, instanceId); + return instances.get(key, new Callable() { + + @Override + public MicroserviceInstance call() throws Exception { + logger.debug("get microservice instance from SC"); + return getMicroserviceInstanceFromSC(serviceId, instanceId); + } + + }); + } catch (ExecutionException e) { + logger.error("get microservice from cache failed:" + String.format("%s@%s", serviceId, instanceId)); + return null; + } + } + + private static MicroserviceInstance getMicroserviceInstanceFromSC(String serviceId, String instanceId) { + return RegistryUtils.getServiceRegistryClient().findServiceInstance(serviceId, instanceId); + } + +} diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java index 139cd6e6fe5..d9107a293f6 100644 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java +++ b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/LocalServiceRegistryClientImpl.java @@ -334,4 +334,12 @@ public boolean updateInstanceProperties(String microserviceId, String microservi } return true; } + + @Override + public MicroserviceInstance findServiceInstance(String serviceId, String instanceId) { + Map instances = microserviceInstanceMap.get(serviceId); + return instances.get(instanceId); + } + + } diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/ServiceRegistryClient.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/ServiceRegistryClient.java index c67be3dc727..4666f0a1b5d 100644 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/ServiceRegistryClient.java +++ b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/ServiceRegistryClient.java @@ -123,4 +123,12 @@ void watch(String selfMicroserviceId, AsyncResultCallback findServiceInstance(String consumerId, String appId, String serviceName, String versionRule); + + /** + * 通过serviceid, instanceid 获取instance对象。 + * @param serviceId + * @param instanceId + * @return MicroserviceInstance + */ + MicroserviceInstance findServiceInstance(String serviceId, String instanceId); } diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java index e1fbe085afe..9b2c1497f00 100644 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java +++ b/service-registry/src/main/java/io/servicecomb/serviceregistry/client/http/ServiceRegistryClientImpl.java @@ -49,6 +49,7 @@ import io.servicecomb.serviceregistry.api.response.GetSchemaResponse; import io.servicecomb.serviceregistry.api.response.GetServiceResponse; import io.servicecomb.serviceregistry.api.response.HeartbeatResponse; +import io.servicecomb.serviceregistry.api.response.MicroserviceInstanceResponse; import io.servicecomb.serviceregistry.api.response.MicroserviceInstanceChangedEvent; import io.servicecomb.serviceregistry.api.response.RegisterInstanceResponse; import io.servicecomb.serviceregistry.client.ClientException; @@ -628,4 +629,28 @@ public boolean updateInstanceProperties(String microserviceId, String microservi } return false; } + + @Override + public MicroserviceInstance findServiceInstance(String serviceId, String instanceId) { + try { + Holder holder = new Holder<>(); + IpPort ipPort = ipPortManager.getAvailableAddress(false); + CountDownLatch countDownLatch = new CountDownLatch(1); + RestUtils.get(ipPort, + String.format(Const.REGISTRY_API.MICROSERVICE_INSTANCE_OPERATION_ONE, serviceId, instanceId), + new RequestParam().addHeader("X-ConsumerId", serviceId), + syncHandler(countDownLatch, MicroserviceInstanceResponse.class, holder)); + countDownLatch.await(); + if (null != holder.value) { + return holder.value.getInstance(); + } + return null; + } catch (Exception e) { + LOGGER.error("get instance from sc failed"); + return null; + } + + } + + } diff --git a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceInstanceRegisterTask.java b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceInstanceRegisterTask.java index d6250c8e603..8e886070c14 100644 --- a/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceInstanceRegisterTask.java +++ b/service-registry/src/main/java/io/servicecomb/serviceregistry/task/MicroserviceInstanceRegisterTask.java @@ -66,7 +66,7 @@ protected boolean doRegister() { microserviceInstance.setHostName(hostName); microserviceInstance.getHealthCheck().setInterval(serviceRegistryConfig.getHeartbeatInterval()); microserviceInstance.getHealthCheck().setTimes(serviceRegistryConfig.getResendHeartBeatTimes()); - + String instanceId = srClient.registerMicroserviceInstance(microserviceInstance); if (StringUtils.isEmpty(instanceId)) { LOGGER.error("Register microservice instance failed. microserviceId={}", diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestMicroserviceInstanceResponse.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestMicroserviceInstanceResponse.java new file mode 100644 index 00000000000..fe4a5fb7196 --- /dev/null +++ b/service-registry/src/test/java/io/servicecomb/serviceregistry/api/response/TestMicroserviceInstanceResponse.java @@ -0,0 +1,35 @@ +/* + * Copyright 2017 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.servicecomb.serviceregistry.api.response; + +import org.junit.Assert; +import org.junit.Test; + +import io.servicecomb.serviceregistry.api.registry.MicroserviceInstance; + +public class TestMicroserviceInstanceResponse { + + + @Test + public void testMicroserviceInstanceResponse() + { + MicroserviceInstance microserviceInstance = new MicroserviceInstance(); + MicroserviceInstanceResponse microserviceInstanceResponse = new MicroserviceInstanceResponse(); + microserviceInstanceResponse.setInstance(microserviceInstance); + Assert.assertNotNull(microserviceInstanceResponse.getInstance()); + } +} diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/LocalServiceRegistryClientImplTest.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/client/LocalServiceRegistryClientImplTest.java index e6b8ae582fc..6cb4b4b4de9 100644 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/LocalServiceRegistryClientImplTest.java +++ b/service-registry/src/test/java/io/servicecomb/serviceregistry/client/LocalServiceRegistryClientImplTest.java @@ -145,4 +145,14 @@ public void registerSchema_normal() { Assert.assertTrue(registryClient.registerSchema(v1.getServiceId(), "sid", "content")); } + + @Test + public void testFindServiceInstance() + { + Microservice microservice = mockRegisterMicroservice(appId, microserviceName, "1.0.0"); + MicroserviceInstance instance = new MicroserviceInstance(); + instance.setServiceId(microservice.getServiceId()); + String instanceId = registryClient.registerMicroserviceInstance(instance); + Assert.assertNotNull(registryClient.findServiceInstance(microservice.getServiceId(), instanceId)); + } } diff --git a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java b/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java index 43835b7cfba..2dd6227fa09 100644 --- a/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java +++ b/service-registry/src/test/java/io/servicecomb/serviceregistry/client/http/TestServiceRegistryClientImpl.java @@ -248,4 +248,11 @@ void doRun(java.util.List events) { } }.run(); } + + + @Test + public void testFindServiceInstance() + { + Assert.assertNull(oClient.findServiceInstance("aaa","bbb")); + } }