Search code examples
spring-bootspring-securityspring-boot-admin

Spring boot admin client not able to register with spring boot admin


I have a spring boot admin server application running which is secured by using spring security basic auth. When I am trying to register a spring boot admin client it's not able to register itself with spring boot admin. Here are the detail about both the application.

Spring Boot Admin Server

pom.xml

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
<properties>
    <spring-boot-admin.version>2.7.4</spring-boot-admin.version>
</properties>
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>

application.yml

spring:
  security:
    user:
      name: admin
      password: admin@123
  application:
    name: spring-boot-admin

server:
  port: 8081

Main class

@SpringBootApplication
@EnableAdminServer
public class AdminServer {
    public static void main(String[] args) {
        SpringApplication.run(AdminServer.class, args);
    }
}

With the above configuration Spring boot admin UI is secured with basic auth.

Spring Boot Admin Client

pom.xml

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
<properties>
    <spring-boot-admin.version>2.7.4</spring-boot-admin.version>
</properties>
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
        </dependency>
    </dependencies>

application.yml

spring:
  boot:
    admin:
      client:
        url: http://localhost:8081
        username: admin
        password: admin@123
management:
  endpoints:
    web:
      exposure:
        include: loggers, health, info
  endpoint:
    loggers:
      enabled: true
    health:
      show-details: always

After turning on the debug log on spring boot admin client, I see the following error

2023-06-09 16:57:55.116 DEBUG 23402 --- [gistrationTask1] o.s.web.client.RestTemplate              : HTTP POST http://localhost:8081/instances
2023-06-09 16:57:55.117 DEBUG 23402 --- [gistrationTask1] o.s.web.client.RestTemplate              : Accept=[application/json, application/*+json]
2023-06-09 16:57:55.117 DEBUG 23402 --- [gistrationTask1] o.s.web.client.RestTemplate              : Writing [Application(name=admin-client, managementUrl=http://localhost:8080/actuator, healthUrl=http://localhost:8080/actuator/health, serviceUrl=http://localhost:8080/)] as "application/json"
2023-06-09 16:57:55.118 DEBUG 23402 --- [gistrationTask1] s.n.www.protocol.http.HttpURLConnection  : sun.net.www.MessageHeader@6f223fc18 pairs: {POST /instances HTTP/1.1: null}{Authorization: Basic YWRtaW46YWRtaW5AMTIz}{Accept: application/json}{Content-Type: application/json}{User-Agent: Java/17.0.6}{Host: localhost:8081}{Connection: keep-alive}{Content-Length: 220}
2023-06-09 16:57:55.121 DEBUG 23402 --- [gistrationTask1] s.n.www.protocol.http.HttpURLConnection  : sun.net.www.MessageHeader@4f08eaf016 pairs: {null: HTTP/1.1 401}{Vary: Origin}{Vary: Access-Control-Request-Method}{Vary: Access-Control-Request-Headers}{Set-Cookie: JSESSIONID=F1EE36B57198F3F2D2DDBB3DDE33A403; Path=/; HttpOnly}{X-Content-Type-Options: nosniff}{X-XSS-Protection: 1; mode=block}{Cache-Control: no-cache, no-store, max-age=0, must-revalidate}{Pragma: no-cache}{Expires: 0}{X-Frame-Options: DENY}{WWW-Authenticate: Basic realm="Realm"}{Content-Length: 0}{Date: Fri, 09 Jun 2023 11:27:55 GMT}{Keep-Alive: timeout=60}{Connection: keep-alive}
2023-06-09 16:57:55.121 DEBUG 23402 --- [gistrationTask1] o.s.web.client.RestTemplate              : Response 401 UNAUTHORIZED

I am not able to figure out what am I missing. Without auth the spring boot admin client is able to register with spring boot admin.


Solution

  • This is resolved now. Followed spring boot admin reference http://docs.spring-boot-admin.com/2.7.4/#_securing_spring_boot_admin_server. After adding SecurityConfig it's working as expected.

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig {
    
        private final AdminServerProperties adminServer;
    
        private final SecurityProperties security;
    
        @Value("${spring.security.user.name}")
        private String username;
    
        @Value("${spring.security.user.password}")
        private String password;
        public SecurityConfig(AdminServerProperties adminServer, SecurityProperties security) {
            this.adminServer = adminServer;
            this.security = security;
        }
    
        @Bean
        protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
            SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
            successHandler.setTargetUrlParameter("redirectTo");
            successHandler.setDefaultTargetUrl(this.adminServer.path("/"));
    
            http.authorizeRequests(
                            (authorizeRequests) -> authorizeRequests.antMatchers(this.adminServer.path("/assets/**")).permitAll()
                                    .antMatchers(this.adminServer.path("/actuator/info")).permitAll()
                                    .antMatchers(this.adminServer.path("/actuator/health")).permitAll()
                                    .antMatchers(this.adminServer.path("/login")).permitAll().anyRequest().authenticated()
                    ).formLogin(
                            (formLogin) -> formLogin.loginPage(this.adminServer.path("/login")).successHandler(successHandler).and()
                    ).logout((logout) -> logout.logoutUrl(this.adminServer.path("/logout")))
                     .httpBasic(Customizer.withDefaults())
                    .csrf((csrf) -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                            .ignoringRequestMatchers(
                                    new AntPathRequestMatcher(this.adminServer.path("/instances"),
                                            POST.toString()),
                                    new AntPathRequestMatcher(this.adminServer.path("/instances/*"),
                                            DELETE.toString()),
                                    new AntPathRequestMatcher(this.adminServer.path("/actuator/**"))
                            )); // <4>
    
    
            return http.build();
    
        }
    
        @Bean
        public InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) {
            UserDetails user = User.withUsername(username).password(passwordEncoder.encode(password)).roles("USER").build();
            return new InMemoryUserDetailsManager(user);
        }
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    }