configurando o春季启动管理:服务器e客户端
#java #springboot #服务器 #client

有几种监视分布式系统的方法,而为了简单的配置和可靠性,我很高兴我是Spring Boot Admin。

在本教程中,我们将使用Spring Boot创建两个应用程序,一个是监视服务器,另一个将是应注册以进行监视的客户。

我们还将借此机会使用Spring Security和#Maven实现安全层,我们可以使应用程序的构建单独执行。

使用的版本

  • 春季靴子:2.7.10
  • Spring Boot Admin服务器:2.7.10
  • Spring Boot Admin客户端:2.7.10

配置服务器

使用Spring Initilizr创建一个具有以下依赖性的项目:

  • 入门网络
  • 入门安全
  • 管理启动服务器

Image description

检查是否添加了与Spring Boot Admin相关的依赖性:

...
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-server-ui</artifactId>
</dependency>
...

在某些配置类中添加注释@enableadminserver:

...

@EnableAdminServer
@SpringBootApplication
public class SpringBootAdminServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootAdminServerApplication.class, args);
    }

}

使用application.yaml文件配置应用程序行为。当我们添加安全层时,我们必须创建一个用户名和密码才能在服务器上登录,并且对于配置服务器和客户端之间的通信登录也是必要的。

server:
  port: 8081
  servlet:
    context-path: /admin-console
spring:
  security:
    user:
      # Configura o login do servidor.
      name: ${SBA_SERVER_USERNAME}
      password: ${SBA_SERVER_PASSWORD}
  boot:
    admin:
      client:
        # Necessários para que o cliente possa se registrar na api do servidor protegido.
        username: ${SBA_SERVER_USERNAME}
        password: ${SBA_SERVER_PASSWORD}
        instance:
          metadata:
            user:
              # Necessários para que o servidor possa acessar os endpoints protegidos do cliente.
              name: ${SBA_CLIENT_USERNAME}
              password: ${SBA_CLIENT_PASSWORD}

# LOG
logging:
  file:
    name: ${user.home}/logs/admin/sba-server.log
  level:
    root: info
    web: info
    dev.marksduarte: info
    org.springframework: info
  charset:
    file: utf-8

现在,让我们通过创建类并在@configuration Notes(ProxyBeanMethods = false)上禁用Bean代理来设置Spring Security,因为当我们与@Bean Self -containing一起工作时,我们可以避免处理CGLIB SubClass。< /p。< /p >

我们还将允许访问登录路线资产和禁用保护 csrf ©all http post 和 http delete 在客户实例中。

package dev.marksduarte.springbootadminserver;

import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration(proxyBeanMethods = false)
public class SecurityConfig {

    private final AdminServerProperties adminServer;

    public SecurityConfig(AdminServerProperties adminServer) {
        this.adminServer = adminServer;
    }

    @Bean
    protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setTargetUrlParameter("redirectTo");
        successHandler.setDefaultTargetUrl(this.adminServer.path("/"));

        http.authorizeHttpRequests(authorizeRequests -> authorizeRequests
                        .requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/assets/**")))
                        .permitAll()
                        .requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/login")))
                        .permitAll()
                        .anyRequest()
                        .authenticated())
                .formLogin(formLogin -> formLogin.loginPage(this.adminServer.path("/login"))
                        .successHandler(successHandler))
                .logout(logout -> logout.logoutUrl(this.adminServer.path("/logout")))
                .httpBasic(Customizer.withDefaults())
                .csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                        .ignoringRequestMatchers(
                                new AntPathRequestMatcher(this.adminServer.path("/instances"), HttpMethod.POST.toString()),
                                new AntPathRequestMatcher(this.adminServer.path("/instances/*"), HttpMethod.DELETE.toString()),
                                new AntPathRequestMatcher(this.adminServer.path("/actuator/**"))));

        return http.build();
    }
}

好吧,现在您正在运行应用程序,并检查管理服务器是否可以通过http://localhost:8081/admin-console的地址访问,并使用配置中的用户和密码登录。< /p>

配置客户

使用Spring Initilizr创建一个具有以下依赖性的项目:

  • 入门网络
  • 起动器执行器
  • 入门安全
  • 管理启动客户端

如果添加了Spring Boot Admin Client和Spring Actor的依赖,请查看您的pom.xml文件:

...
<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>
    <version>2.7.10</version>
</dependency>
...

现在让我们配置从application.yaml文件开始的系统:

## INFO ENDPOINT
## Aqui configuramos as informações sobre o sistema, como nome, descrição, versão e etc.
info:
  name: Spring Boot Admin Client
  description: Sistema Cliente
  version: @project.version@

server:
  port: 8080
  servlet:
    context-path: /admin-client

spring:
  # Configuração básica do Spring Security.
  security:
    user:
      name: ${SBA_CLIENT_USERNAME}
      password: ${SBA_CLIENT_PASSWORD}
  boot:
    admin:
      client:
        enabled: true
        # URL do servidor que o cliente deve se registrar.
        url: http://localhost:8081/admin-console
        username: ${SBA_SERVER_USERNAME}
        password: ${SBA_SERVER_PASSWORD}
        instance:
          # URL base para calcular o service-url com o qual se registrar. O caminho é inferido em tempo de execução e anexado à url base.
          service-base-url: http://localhost:8080
          # Essas informações são passadas ao servidor para que ele possa fazer o acesso aos endpoints do sistema cliente.
          metadata:
            user:
              name: ${SBA_SERVER_USERNAME}
              password: ${SBA_SERVER_PASSWORD}
        auto-deregistration: true

## APP
app:
  cors-origins:
    - http://localhost
  cors-methods:
    - GET
    - POST
    - PUT
    - DELETE
    - OPTIONS
  cors-headers:
    - Authorization
    - Content-Type
    - Content-Length
    - X-Requested-With

## ACTUATOR
management:
  info:
    env:
      # Desde o Spring Boot 2.6, o env info é desabilitado por padrão.
      enabled: true
  endpoint:
    health:
      show-details: ALWAYS
      enabled: true
    shutdown:
      enabled: true
    logfile:
      enabled: true
      external-file: logs/sba-client.log
  endpoints:
    web:
      exposure:
        # Liberamos todos os endpoints, mas lembre-se, em produção não se deve fazer isso.
        include: "*"
      cors:
        allowed-headers: ${app.cors-headers}
        allowed-methods: ${app.cors-methods}
        allowed-origins: ${app.cors-origins}

## LOG
logging:
  file:
    name: logs/sba-client.log
    path: logs
  level:
    root: info
    web: info
    dev.marksduarte: info
  charset:
    file: utf-8
  logback:
    rollingpolicy:
      clean-history-on-start: true
      max-file-size: 10MB

要简化,我们将启用“/执行器/**”端点的所有请求:

...

@EnableWebSecurity
@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf()
                .disable()
                .authorizeHttpRequests()
                .antMatchers("/actuator/**")
                .permitAll();
        return http.build();
    }
}

好吧,这已经足以在服务器上注册我们的客户端应用程序。

但是,如果发生任何例外类型: httpmesagenotwratible exception 或响应错误http 416在尝试访问日志文件时,它不会害怕,如果您的系统具有某些类杰克逊配置,则可能发生这种情况扩展了 webmvcconfiguratationsupport

在这种情况下,可以用Spring Boot的模式禁用此类的实例。

要纠正这种问题,我们可以创建一个自定义bean并替换系统初始化中创建的标准配置。

@Configuration
public class JacksonConfig extends WebMvcConfigurationSupport {

    private final Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();

    private static final List<MediaType> MEDIA_TYPE_LIST = List.of(
            MediaType.ALL,
            MediaType.parseMediaType("application/vnd.spring-boot.actuator.v2+json")
    );

    @Bean
    public MappingJackson2HttpMessageConverter customMappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = actuatorConverter();
        converter.setSupportedMediaTypes(MEDIA_TYPE_LIST);
        return converter;
    }

    private MappingJackson2HttpMessageConverter actuatorConverter() {
        return new MappingJackson2HttpMessageConverter(builder.build()) {
            @Override
            protected boolean canWrite(MediaType mediaType) {
                // O método super, retorna true se for null.
                // Assim evitamos a exceção _HttpMessageNotWritableException_ caso o Content-Type 'null' seja enviado.
                return mediaType != null && super.canWrite(mediaType);
            }
        };
    }

    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        /*
        Remove somente o MappingJackson2HttpMessageConverter
padrão e substitui pelo customMappingJackson2HttpMessageConverter.
         */
        var defaultHttpConverterOpt = converters.stream()
                .filter(MappingJackson2HttpMessageConverter.class::isInstance)
                .findFirst();

        defaultHttpConverterOpt.ifPresent(converters::remove);
        converters.add(customMappingJackson2HttpMessageConverter());
    }
}

Image description

Image description

目前是圣。 ato©更多! ;)