Spring Cloud 2021.X Eureka Cannot execute request on any known server

    最近用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();
    }

}

    問題順利解決。

    注意:這只是開發環境,所以直接禁用,如果是生產環境,建議根據實際需求配置跨域,避免不必要的安全問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章