Skip to content

Commit

Permalink
Add option to configure the public url
Browse files Browse the repository at this point in the history
In case the server is running behind a reverse proxy doing path-
rewriting, the public url can now be specified so that back-references
(e.g. base-path) is now correct.

closes #983
  • Loading branch information
joshiste committed Nov 11, 2018
1 parent 66e4189 commit 641f660
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 9 deletions.
4 changes: 4 additions & 0 deletions spring-boot-admin-docs/src/main/asciidoc/server.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
| Headers not to be forwarded when making requests to clients.
| `"Cookie", "Set-Cookie", "Authorization"

| spring.boot.admin.ui.public-url
| Brand to be shown in then navbar.
| If running behind a reverse proxy (using path rewriting) this can be used to make correct self references. If the host/port is omitted it will be inferred from the request.

| spring.boot.admin.ui.brand
| Brand to be shown in then navbar.
| `"<img src="assets/img/icon-spring-boot-admin.svg"><span>Spring Boot Admin</span>"`
Expand Down
6 changes: 6 additions & 0 deletions spring-boot-admin-server-ui/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
2 changes: 1 addition & 1 deletion spring-boot-admin-server-ui/src/main/frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

<html>
<head>
<base th:href="@{${adminContextPath} + '/'}" href="/">
<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">
Expand Down
2 changes: 1 addition & 1 deletion spring-boot-admin-server-ui/src/main/frontend/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

<html>
<head>
<base th:href="@{${adminContextPath} + '/'}" href="/">
<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">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ public AdminServerUiAutoConfiguration(AdminServerUiProperties uiProperties,
@ConditionalOnMissingBean
public UiController homeUiController() throws IOException {
return new UiController(
this.adminServerProperties.getContextPath(),
this.uiProperties.getPublicUrl() !=
null ? this.uiProperties.getPublicUrl() : this.adminServerProperties.getContextPath(),
this.uiProperties.getTitle(),
this.uiProperties.getBrand(),
this.uiExtensions()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ public class AdminServerUiProperties {
*/
private String brand = "<img src=\"assets/img/icon-spring-boot-admin.svg\"><span>Spring Boot Admin</span>";

/**
* If running behind a reverse proxy (using path rewriting) this can be used to output correct self references.
* If the host/port is omitted it will be inferred from the request.
*/
@Nullable
private String publicUrl = null;

/**
* Wether the thymeleaf templates should be cached.
*/
private boolean cacheTemplates = true;

private final Cache cache = new Cache();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,21 @@
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;

@AdminController
public class UiController {
private final String adminContextPath;
private final String publicUrl;
private final List<UiExtension> cssExtensions;
private final List<UiExtension> jsExtensions;
private final Map<String, Object> uiSettings;

public UiController(String adminContextPath, String title, String brand, List<UiExtension> uiExtensions) {
this.adminContextPath = adminContextPath;
public UiController(String publicUrl, String title, String brand, List<UiExtension> uiExtensions) {
this.publicUrl = publicUrl;
this.uiSettings = new HashMap<>();
this.uiSettings.put("title", title);
this.uiSettings.put("brand", brand);
Expand All @@ -52,9 +54,19 @@ public UiController(String adminContextPath, String title, String brand, List<Ui
.collect(Collectors.toList());
}

@ModelAttribute(value = "adminContextPath", binding = false)
public String getAdminContextPath() {
return adminContextPath;
@ModelAttribute(value = "baseUrl", binding = false)
public String getBaseUrl(UriComponentsBuilder uriBuilder) {
UriComponents publicComponents = UriComponentsBuilder.fromUriString(publicUrl).build();
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();
}

@ModelAttribute(value = "uiSettings", binding = false)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2014-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package de.codecentric.boot.admin.server.ui.web;

import de.codecentric.boot.admin.server.web.servlet.AdminControllerHandlerMapping;

import java.util.Collections;
import org.junit.Test;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;

public class UiControllerTest {

@Test
public void should_use_default_url() throws Exception {
MockMvc mockMvc = setupController("");

mockMvc.perform(get("http://example/"))
.andExpect(status().isOk())
.andExpect(view().name("index"))
.andExpect(model().attribute("baseUrl", "http://example/"));
}

@Test
public void should_use_path_from_public_url() throws Exception {
MockMvc mockMvc = setupController("/public");

mockMvc.perform(get("http://example/"))
.andExpect(status().isOk())
.andExpect(view().name("index"))
.andExpect(model().attribute("baseUrl", "http://example/public/"));
}

@Test
public void should_use_host_and_path_from_public_url() throws Exception {
MockMvc mockMvc = setupController("http://public/public");

mockMvc.perform(get("http://example/"))
.andExpect(status().isOk())
.andExpect(view().name("index"))
.andExpect(model().attribute("baseUrl", "http://public/public/"));
}

private MockMvc setupController(String publicUrl) {
return MockMvcBuilders.standaloneSetup(new UiController(publicUrl, "", "", Collections.emptyList()))
.setCustomHandlerMapping(() -> new AdminControllerHandlerMapping(""))
.build();
}
}

0 comments on commit 641f660

Please sign in to comment.