最近用SpringCloud 2021搭建项目玩,遇到一个有点烦人的问题,卡了我一个小时,不是大问题,但值的记录一下,主要是思路和突破口的寻找。
先说一下前因后果吧。找了一圈各类注册中心,发现符合我目前需求的依然是Eureka,出于安全原因,我需要开启Eureka Server的安全授权。个人觉得,无论是内网环境还是公网测试环境,都应该严格控制注册列表权限(这也是为什么我不用nacos的原因之一,nacos设置一堆东西,实在烦不胜烦,腾讯云提供的免费nacos竟然不支持设置安全相关的,只支持关闭公网访问,如果遇到坏蛋搞破坏……哈,扯远了)。所以在Eureka Server的pom.xml增加了以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
然后调整eureka client端的url配置:
# 注册中心
eureka.client.register-with-eureka=true
eureka.client.service-url.defaultZone=http://user:user@localhost:8761/eureka/
期望的结果是能够正常注册上去,然而控制台给我一堆错误:
com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server
at com.netflix.discovery.shared.transport.decorator.RetryableEurekaHttpClient.execute(RetryableEurekaHttpClient.java:112) ~[eureka-client-1.10.17.jar:1.10.17]
at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.register(EurekaHttpClientDecorator.java:56) ~[eureka-client-1.10.17.jar:1.10.17]
at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$1.execute(EurekaHttpClientDecorator.java:59) ~[eureka-client-1.10.17.jar:1.10.17]
at com.netflix.discovery.shared.transport.decorator.SessionedEurekaHttpClient.execute(SessionedEurekaHttpClient.java:77) ~[eureka-client-1.10.17.jar:1.10.17]
at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.register(EurekaHttpClientDecorator.java:56) ~[eureka-client-1.10.17.jar:1.10.17]
at com.netflix.discovery.DiscoveryClient.register(DiscoveryClient.java:876) ~[eureka-client-1.10.17.jar:1.10.17]
at com.netflix.discovery.InstanceInfoReplicator.run(InstanceInfoReplicator.java:121) ~[eureka-client-1.10.17.jar:1.10.17]
at com.netflix.discovery.InstanceInfoReplicator$1.run(InstanceInfoReplicator.java:101) [eureka-client-1.10.17.jar:1.10.17]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_261]
at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) [na:1.8.0_261]
at java.util.concurrent.FutureTask.run(FutureTask.java) [na:1.8.0_261]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_261]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_261]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_261]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_261]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_261]
跟踪了一波源码发现用户和密码信息配置正确,Eureka Client底层源码(org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient):
@Override
public EurekaHttpResponse<Void> register(InstanceInfo info) {
String urlPath = serviceUrl + "apps/" + info.getAppName();
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.ACCEPT_ENCODING, "gzip");
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
ResponseEntity<Void> response = restTemplate.exchange(urlPath, HttpMethod.POST, new HttpEntity<>(info, headers),
Void.class);
return anEurekaHttpResponse(response.getStatusCodeValue()).headers(headersOf(response)).build();
}
通过Debug顺利拿到eureka-client请求eureka-server的相关信息,发现URL并没有问题,多次尝试后,排除Eureka-Client出问题。那么,只剩下Server端的问题了.
观察Eureka-Server的控制台,发现并没有明显异常,随即把Eureka-Server的日志级别设置为debug级别,仔细观察控制台,发现有如下关键日志:
Invalid CSRF token found for
啊哈,竟然是跨域了?早起版本并没有这个问题。顺利跟踪到日志输出的类:org.springframework.security.web.csrf.CsrfFilter
问题范围再次缩小,spring-security的跨域问题,在eureka-server工程增加如下代码:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @author RippleChan
* @date 2022/1/4 12:33 AM
*/
@Configuration
@EnableWebSecurity
public class EurekaSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
}
问题顺利解决。
注意:这只是开发环境,所以直接禁用,如果是生产环境,建议根据实际需求配置跨域,避免不必要的安全问题。