應(yīng)用場(chǎng)景:
采用Spring Boot Admin監(jiān)控微服務(wù)運(yùn)行情況,部署兩個(gè)Admin服務(wù),然后通過Nginx進(jìn)行負(fù)載均衡,從而實(shí)現(xiàn)Admin服務(wù)高可用。在Nginx中配置SSL證書,通過HTTPS協(xié)議進(jìn)行訪問。
問題描述:
在瀏覽器中訪問https://admin./#/applications,頁面無法顯示,查看頁面請(qǐng)求信息后發(fā)現(xiàn)瀏覽器在加載當(dāng)前頁面的資源文件時(shí)使用的是http://admin.的地址,因?yàn)檎?qǐng)求協(xié)議http和地址欄中的請(qǐng)求協(xié)議https不一樣,所以瀏覽器認(rèn)為是跨域訪問,阻止了請(qǐng)求,導(dǎo)致頁面無法顯示。
原因分析:
根據(jù)瀏覽器中的錯(cuò)誤提示,可以斷定是Admin的頁面中在某個(gè)地方設(shè)置了請(qǐng)求地址,從而導(dǎo)致異步請(qǐng)求的地址和地址欄中的地址不一致。查看頁面源碼,可以發(fā)現(xiàn)
<base href="http://admin./">
這個(gè)地址中使用的是http協(xié)議,很有可能是異步請(qǐng)求的時(shí)候就使用了這里的值。接下來我們看看能否從官方源碼中找到答案,下面是Admin前端代碼中index.html的部分代碼。
<head>
<base th:href="${baseUrl}" href="/">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="format-detection" content="telephone=no,email=no">
<meta name="theme-color" content="#42d3a5">
<!-- include extensions -->
<th:block th:each="cssExtension : ${cssExtensions}">
<link rel="preload" th:href="'extensions/' + ${cssExtension.resourcePath}" as="style">
</th:block>
<th:block th:each="jsExtension : ${jsExtensions}">
<link rel="preload" th:href="'extensions/' + ${jsExtension.resourcePath}" as="script">
</th:block>
<th:block th:each="cssExtension : ${cssExtensions}">
<link th:href="'extensions/' + ${cssExtension.resourcePath}" rel="stylesheet">
</th:block>
<link rel="shortcut icon" th:href="${uiSettings.favicon}" type="image/png">
<title th:text="${uiSettings.title}">Spring Boot Admin</title>
</head>
從上面的代碼中可以發(fā)現(xiàn)${baseUrl} 是一個(gè)變量,在頁面訪問的時(shí)候轉(zhuǎn)換成了訪問的網(wǎng)址,接下來我們?cè)倏催@個(gè)值是如何設(shè)置的,通過開發(fā)工具在項(xiàng)目中搜索baseUrl,我們找到了一個(gè)baseUrl接口,方法路徑de.codecentric.boot.admin.server.ui.web.UiController#getBaseUrl
@ModelAttribute(value = "baseUrl", binding = false)
public String getBaseUrl(UriComponentsBuilder uriBuilder) {
UriComponents publicComponents = UriComponentsBuilder.fromUriString(this.publicUrl).build();
if (publicComponents.getScheme() != null) {
uriBuilder.scheme(publicComponents.getScheme());
}
if (publicComponents.getHost() != null) {
uriBuilder.host(publicComponents.getHost());
}
if (publicComponents.getPort() != -1) {
uriBuilder.port(publicComponents.getPort());
}
if (publicComponents.getPath() != null) {
uriBuilder.path(publicComponents.getPath());
}
return uriBuilder.path("/").toUriString();
}
在方法中打上斷點(diǎn),發(fā)現(xiàn)每次打開Admin頁面的時(shí)候,都會(huì)請(qǐng)求這個(gè)方法,而且這個(gè)方法返回的正是頁面中地址:http://admin./。既然這個(gè)地址是從后臺(tái)接口中獲取的,那么是否可以通過某個(gè)配置項(xiàng)來設(shè)置呢,在方法中我們可以看到publicUrl這個(gè)參數(shù)會(huì)影響生成的地址,然后嘗試在application.yml配置文件中輸入這個(gè)配置項(xiàng),根據(jù)提示發(fā)現(xiàn)public-url這個(gè)配置項(xiàng),并對(duì)其進(jìn)行賦值
spring:
boot:
admin:
ui:
public-url: https://admin./
配置完成后重啟服務(wù),再次訪問Admin頁面,頁面可以正常顯示,再看頁面源碼中的地址已經(jīng)變成了
<base href="https://admin./">
查找資料發(fā)現(xiàn)public-url這個(gè)值就是用于構(gòu)建頁面中href的值,它的作用是在反向代理的情況下設(shè)置請(qǐng)求地址。如果不進(jìn)行配置,那么主機(jī)和端口都可以從瀏覽器地址欄中的訪問地址中獲取,但是請(qǐng)求協(xié)議使用的是默認(rèn)的http,這時(shí)如果和地址欄中訪問地址的協(xié)議不一致,就會(huì)導(dǎo)致頁面中的異步請(qǐng)求跨域,被瀏覽器攔截。
解決方案:
在Admin服務(wù)端通過spring.boot.admin.ui.public-url配置請(qǐng)求地址,避免跨域訪問。
spring:
boot:
admin:
ui:
public-url: https://admin./
另外還有幾個(gè)關(guān)于Admin服務(wù)端頁面的配置 spring.boot.admin.ui.brand 用于設(shè)置頁面中的LOGO spring.boot.admin.ui.title 用于設(shè)置瀏覽器頁面標(biāo)簽 spring.boot.admin.ui.favicon 用于設(shè)置瀏覽器頁面標(biāo)簽中顯示的圖標(biāo)
軟件版本:
JDK:1.8 Spring Boot:2.1.9.RELEASE Spring Boot Admin:2.1.6 Nginx:1.20.1
參考資料:
[1]: https://blog.csdn.net/u014217825/article/details/103061261 [2]: https:///mirrors/spring-boot-admin
|