概述
声明
不要使用此方法进行违法操作,本文只提供学习Eureka相关知识,涉及实验纯属个人娱乐。
前言
众所周知,在SpringCloud中使用的是json+http进行信息的交互,那么我们猜想,client端在启动时会将自己的信息以json方式发送给Server端,Server端接受后将信息存放到Cache中,那么等于注册了一个服务到Server端;再近一步,我们还可以一直发送/health的心跳检测,那么这个服务会一直存在于Eureka维护的核心容器中。这还不是最骚的,我们还可以直接强制下线某个微服务,然后自己"冒名顶替",这个黑户服务通过ip转发实现劫持。。。
所以你的微服务架构可千万不要"裸奔",安全第一位。
核心知识点
EurekaClient启动
EurekaClient客户端启动时会注入一些外部Bean:spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration,
org.springframework.cloud.netflix.eureka.reactive.EurekaReactiveDiscoveryClientConfiguration,
org.springframework.cloud.netflix.eureka.loadbalancer.LoadBalancerEurekaAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration
我们暂时关注EurekaClientAutoConfiguration,EurekaClientAutoConfiguration会注入一些Bean与Configuration,无论是热加载还是非热加载(@ConditionalOnRefreshScope与@ConditionalOnMissingRefreshScope先不多说)都会创建一个CloudEurekaClient对象。
// 例如:@ConditionalOnMissingRefreshScope
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(value = EurekaClient.class,
search = SearchStrategy.CURRENT)
public EurekaClient eurekaClient(ApplicationInfoManager manager,
EurekaClientConfig config) {
return new CloudEurekaClient(manager, config, this.optionalArgs,
this.context);
}
这里面ApplicationInfoManager 包含了实例启动的config与当前实例的基本信息
@Bean
@ConditionalOnMissingBean(value = ApplicationInfoManager.class,
search = SearchStrategy.CURRENT)
@org.springframework.cloud.context.config.annotation.RefreshScope
@Lazy
public ApplicationInfoManager eurekaApplicationInfoManager(
EurekaInstanceConfig config) {
InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
return new ApplicationInfoManager(config, instanceInfo);
}
InstanceInfo包含了当前启动实例所有核心信息。
回到上面new的CloudEurekaClient,此对象在生成时提供方法反射获取HTTPclient:
EurekaHttpClient getEurekaHttpClient() {
if (this.eurekaHttpClient.get() == null) {
try {
Object eurekaTransport = this.eurekaTransportField.get(this);
Field registrationClientField = ReflectionUtils.findField(eurekaTransport.getClass(), "registrationClient");
ReflectionUtils.makeAccessible(registrationClientField);
this.eurekaHttpClient.compareAndSet((Object)null, (EurekaHttpClient)registrationClientField.get(eurekaTransport));
} catch (IllegalAccessException var3) {
log.error("error getting EurekaHttpClient", var3);
}
}
return (EurekaHttpClient)this.eurekaHttpClient.get();
}
EurekaHttpClient功能强大(相当于注册发现的implement),他可以帮助Client进行注册,发现,取消,移除,状态修改,服务发现(全量,增量)等基础功能实现,在他的实现类中我们可以看到进行的http处理。
public interface EurekaHttpClient {
EurekaHttpResponse<Void> register(InstanceInfo var1);
EurekaHttpResponse<Void> cancel(String var1, String var2);
EurekaHttpResponse<InstanceInfo> sendHeartBeat(String var1, String var2, InstanceInfo var3, InstanceStatus var4);
EurekaHttpResponse<Void> statusUpdate(String var1, String var2, InstanceStatus var3, InstanceInfo var4);
EurekaHttpResponse<Void> deleteStatusOverride(String var1, String var2, InstanceInfo var3);
EurekaHttpResponse<Applications> getApplications(String... var1);
EurekaHttpResponse<Applications> getDelta(String... var1);
EurekaHttpResponse<Applications> getVip(String var1, String... var2);
EurekaHttpResponse<Applications> getSecureVip(String var1, String... var2);
EurekaHttpResponse<Application> getApplication(String var1);
EurekaHttpResponse<InstanceInfo> getInstance(String var1, String var2);
EurekaHttpResponse<InstanceInfo> getInstance(String var1);
void shutdown();
}
EurekaDiscoveryClientConfiguration注入的Bean当然还包括服务注册的Bean(调用EurekaHttpClient.register的操作)
@Bean
public EurekaServiceRegistry eurekaServiceRegistry() {
return new EurekaServiceRegistry();
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(
value = "spring.cloud.service-registry.auto-registration.enabled",
matchIfMissing = true)
public EurekaAutoServiceRegistration eurekaAutoServiceRegistration(
ApplicationContext context, EurekaServiceRegistry registry,
EurekaRegistration registration) {
return new EurekaAutoServiceRegistration(context, registry, registration);
}
EurekaServiceRegistry包含的方法就是register,deregister,setStatus,getStatus。
DiscoveryClient会调用上面我们的httpclient
boolean register() throws Throwable {
logger.info("DiscoveryClient_{}: registering service...", this.appPathIdentifier);
EurekaHttpResponse httpResponse;
try {
httpResponse = this.eurekaTransport.registrationClient.register(this.instanceInfo);
} catch (Exception var3) {
logger.warn("DiscoveryClient_{} - registration failed {}", new Object[]{this.appPathIdentifier, var3.getMessage(), var3});
throw var3;
}
if (logger.isInfoEnabled()) {
logger.info("DiscoveryClient_{} - registration status: {}", this.appPathIdentifier, httpResponse.getStatusCode());
}
return httpResponse.getStatusCode() == Status.NO_CONTENT.getStatusCode();
}
开始实验
实验一,模拟注册一个微服务
为了方便EurekaHttpClient我们统一参照org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient的代码
public EurekaHttpResponse<Void> register(InstanceInfo info) {
String urlPath = this.serviceUrl + "apps/" + info.getAppName();
HttpHeaders headers = new HttpHeaders();
headers.add("Accept-Encoding", "gzip");
headers.add("Content-Type", "application/json");
ResponseEntity<Void> response = this.restTemplate.exchange(urlPath, HttpMethod.POST, new HttpEntity(info, headers), Void.class, new Object[0]);
return EurekaHttpResponse.anEurekaHttpResponse(response.getStatusCodeValue()).headers(headersOf(response)).build();
}
我们使用的url为http://localhost:7001/eureka/apps/PROVIDER-PAYMENT8002;
通过抓包获取到报文结构:
我们使用此报文
{
"instance": {
"instanceId": "provider-payment8002",
"hostName": "192.168.0.106",
"app": "PROVIDER-PAYMENT8002",
"ipAddr": "192.168.0.106",
"status": "UP",
"overriddenStatus": "UNKNOWN",
"port": {
"$": 8002,
"@enabled": "true"
},
"securePort": {
"$": 443,
"@enabled": "false"
},
。。。
"homePageUrl": "http://192.168.0.106:8002/",
"statusPageUrl": "http://192.168.0.106:8002/actuator/info",
"healthCheckUrl": "http://192.168.0.106:8002/actuator/health",
"vipAddress": "provider-payment8002",
"secureVipAddress": "provider-payment8002",
"isCoordinatingDiscoveryServer": "false",
"lastUpdatedTimestamp": "1584033235520",
"lastDirtyTimestamp": "1584033236309"
}
}
实验二,下线一个微服务
public EurekaHttpResponse<Void> cancel(String appName, String id) {
String urlPath = this.serviceUrl + "apps/" + appName + '/' + id;
ResponseEntity<Void> response = this.restTemplate.exchange(urlPath, HttpMethod.DELETE, (HttpEntity)null, Void.class, new Object[0]);
return EurekaHttpResponse.anEurekaHttpResponse(response.getStatusCodeValue()).headers(headersOf(response)).build();
}
这里的id是什么呢?DiscoveryClient中有,即instanceInfo.getId(),默认是微服务的小写:
void unregister() {
if (this.eurekaTransport != null && this.eurekaTransport.registrationClient != null) {
try {
logger.info("Unregistering ...");
EurekaHttpResponse<Void> httpResponse = this.eurekaTransport.registrationClient.cancel(this.instanceInfo.getAppName(), this.instanceInfo.getId());
logger.info("DiscoveryClient_{} - deregister status: {}", this.appPathIdentifier, httpResponse.getStatusCode());
} catch (Exception var2) {
logger.error("DiscoveryClient_{} - de-registration failed{}", new Object[]{this.appPathIdentifier, var2.getMessage(), var2});
}
}
}
所以我们的url是:http://localhost:7001/eureka/apps/PROVIDER-PAYMENT8002/provider-payment8002
delete请求。
实验三,服务劫持
将生产的8001下线操作http://localhost:7001/eureka/apps/PROVIDER-PAYMENT8001/provider-payment8001,然后启动一个脚本定时任务,先发送请求将自己注册上Eureka,并且每30s发送健康检查,发送请求,请求可以被此第三方服务正常消费,即实验成功。
最后
以上就是幸福宝贝为你收集整理的004. Eureka服务注册与欺骗你的EurekaServer的全部内容,希望文章能够帮你解决004. Eureka服务注册与欺骗你的EurekaServer所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复