Spring Boot Admin Pit Drainage Guide

Keywords: Programming Spring Java github Docker

Spring Boot Admin 1.x has a rudimentary page that people can't bear to look straight at, but when it's updated to the 2.x series, it's as easy to use as a rebirth.

This blog records the pits I personally encountered in using Spring Boot Admin, each pit will be accompanied by a detailed filling method.

Environmental parameters:

  • Spring Boot 2.x

  • Spring Boot Admin 2.x

  • JDK1.8+

  • CentOS

Service Direct Registration Failure

Common registration failures can be categorized into the following two types

  • Spring Boot Admin server and client are not on the same server

  • Suggest that the safety check is not passed

The solution to the first problem:

The boot.admin.client.instance.service-url property must be configured on the client side so that the Spring Boot Admin server can access the client's data over the network (otherwise it will be acquired by default through the host name)

  boot:
    admin:
      client:
        url: ${your spring boot admin url}
        username: ${your spring boot admin username}
        password: ${your spring boot admin password}
        instance:
          prefer-ip: true
          service-url: ${your spring boot client url} 

Solutions to the second problem:

First of all, the security inspection problem is that the server configures the account password, and then the client provides the account password for login to complete the verification when registering.

The implementation of this process, as a Spring family bucket project, is recommended to use Spring Security to solve the problem, so if there is a validation failure, it is mostly Spring Security configuration problems.

Next, I'll show you how to configure the server and client to handle this problem, respectively.

Server Configuration

Loading Spring Security dependencies through maven

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Set the username and password of the server (the client logs in with this account password when registering)

spring:
  security:
    user:
      name: liumapp
      password: superliumapp

Write the Spring Security configuration class

import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

/**
 * file SecuritySecureConfig.java
 * author liumapp
 * github https://github.com/liumapp
 * email liumapp.com@gmail.com
 * homepage http://www.liumapp.com
 * date 2018/11/29
 */
@Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
    private final String adminContextPath;

    public SecuritySecureConfig(AdminServerProperties adminServerProperties) {
        this.adminContextPath = adminServerProperties.getContextPath();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setTargetUrlParameter("redirectTo");
        successHandler.setDefaultTargetUrl(adminContextPath + "/");

        http.authorizeRequests()
                .antMatchers(adminContextPath + "/assets/**").permitAll()
                .antMatchers(adminContextPath + "/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler).and()
                .logout().logoutUrl(adminContextPath + "/logout").and()
                .httpBasic().and()
                .csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .ignoringAntMatchers(
                        adminContextPath + "/instances",
                        adminContextPath + "/actuator/**"
                );
        // @formatter:on
    }
}

The above code, you need to pay attention to an AdminServer Properties class, by browsing part of its source code:

@ConfigurationProperties("spring.boot.admin")
public class AdminServerProperties {
    /**
     * The context-path prefixes the path where the Admin Servers statics assets and api should be
     * served. Relative to the Dispatcher-Servlet.
     */
    private String contextPath = "";
    
    /**
     * The metadata keys which should be sanitized when serializing to json
     */
    private String[] metadataKeysToSanitize = new String[]{".*password$", ".*secret$", ".*key$", ".*$token$", ".*credentials.*", ".*vcap_services$"};

    /**
     * For Spring Boot 2.x applications the endpoints should be discovered automatically using the actuator links.
     * For Spring Boot 1.x applications SBA probes for the specified endpoints using an OPTIONS request.
     * If the path differs from the id you can specify this as id:path (e.g. health:ping).
     */
    private String[] probedEndpoints = {"health", "env", "metrics", "httptrace:trace", "httptrace", "threaddump:dump", "threaddump", "jolokia", "info", "logfile", "refresh", "flyway", "liquibase", "heapdump", "loggers", "auditevents", "mappings", "scheduledtasks", "configprops", "caches", "beans"};
    
    //The following is omitted.
    
}

You can see that AdminServer Properties defines Spring Boot Admin's configuration properties, and login is naturally one of them, so when we write Spring Security configuration classes, we must introduce AdminServer Properties.

At this point, the Spring Boot Admin server's configuration for Spring Security is over. Let's start the client's Security configuration.

Client Configuration

First, for the client, we need to introduce Spring Security dependencies in addition to Spring Boot Admin Client dependencies:

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
    <version>2.0.2</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

On this basis, the account password is set by writing the client application.yml configuration file.

spring:
  boot:
    admin:
      client:
        url: ${your sba server url}
        username: ${your sba username}
        password: ${your sba password}
        instance:
          service-base-url: ${your client url}

Next, configure Spring Security on Client side to allow Server side to read the data exposed by actuator

Add a configuration class:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class SecurityPermitAllConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().permitAll()
                .and().csrf().disable();
    }
}

At this point, the problem of failing to register successfully because of security verification can be solved.

Registered successfully but unable to display logs

There are two reasons for this problem.

  • Client logs are not stored as files

  • After client containerized deployment, log files are not mapped to host disk

For the first case, the solution is relatively simple. The log generated by the system can be saved as a file.

logging:
  file: ./log/client.log
  pattern:
    file: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID}){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx"      

The second situation is more complex. First, we need to divide the tools used to deploy containers, but in general, we can use file mapping directly.

Take docker as an example. In docker, log files are mapped by setting volumes

volumes:
  - ./log:/client/log/

Successful registration but incomplete information display

Occasionally, this happens: Spring Boot Admin client registration server is successful, but the statistics page displays too little data (maybe only the log column)

The reason for this problem is that we do not open the actuator interface address of the client to the server.

The solution is simple, allowing the server to access actuator.

First, we need to ensure that the project has an actuator dependency (generally spring-boot-admin-starter-client itself contains this dependency, so no additional introduction is required):

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Then open the port of actuator and add the following contents to the client-side configuration file:

management:
  endpoints:
    web:
      exposure:
        include: "*"

Considering the difference between client domain name and server domain name, cross-domain configuration classes are added.


import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author liumapp
 * @file CorsConfig.java
 * @email liumapp.com@gmail.com
 * @homepage http://www.liumapp.com
 * @date 2018/8/11
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer {
   
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowCredentials(true)
                .allowedHeaders("*")
                .allowedOrigins("*")
                .allowedMethods("*");

    }
}

The problem can be solved.

Posted by mmonaco on Sun, 04 Aug 2019 20:50:26 -0700