最近用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();
}
}
問題順利解決。
注意:這只是開發環境,所以直接禁用,如果是生產環境,建議根據實際需求配置跨域,避免不必要的安全問題。