--- title: 'Configuración Spring Config Server' --- Configuración del Spring Config Server === * **Coordinador Tecnológico:** Rafael Palau * **Consultores:** Julio Mello Marcos Benítez Marco Aquino Ilse Grau Lauro Segovia Alan Sanier # Contenido [TOC] # Introducción En el documento se dará a conocer la utilización del *Spring Cloud Config* y la configuración necesaria para su funcionamiento. # Objetivo Configurar el servidor y el cliente del config server. # Config Server *Spring Cloud Config* es el enfoque cliente/servidor de Spring para almacenar y proveer configuraciones distribuidas en múltiples aplicaciones y entornos. Idealmente, este almacén de configuración está versionado bajo el control de versiones de Git y se puede modificar mientras se ejecuta la aplicación. *Spring Cloud Config* permite exteriorizar y centralizar la configuración de los módulos en un solo lugar. Por defecto, utiliza un repositorio local en filesystem, pero también puede utilizar Git como repositorio para centralizar la configuración de cada uno de los servicios. ![](https://i.imgur.com/0WAy5Jc.png) Figura 1. Esquema del Spring Cloud Config # Configuración En esta sección se presentan los pasos a seguir para configurar el proyecto servidor y cliente. ## Servidor En servidor de configuración (spring config server) expone un conjunto de recursos para que los servicios (clientes) puedan acceder a sus configuraciones: ### Apis disponibles para obtención de configuraciones ```shell= /{application}/{profile}[/{label}] /{application}-{profile}.yml /{label}/{application}-{profile}.yml /{application}-{profile}.properties /{label}/{application}-{profile}.properties ``` > El cliente que invoca al servicio del config server, agrega el valor de **application** utilizando el nombre configurado en la propiedad spring.application.name. de la misma. > El valor de **profile** es el perfil activo asigando. > El valor de **label** es una etiqueta de Git opcional. Ejemplo de consulta vía HTTP: ```shell= curl --location --request GET 'http://localhost:8888/usuarios-back/lsegovia' \ --header 'Authorization: Basic bWJlbXo6MTIzNDU=' ``` ## Prerrequisito 1. Crear el repositorio Git para almacenar los archivos de configuración. Los archivos de configuración deben ser ordenados en directorios dentro del repositorio y el nombre del directorio debe coincidir con el *application.name* que fue declarado en el **application.properties** del consumidor. Por ejemplo, en el caso de presupuesto, se tiene que crear un directorio **presupuesto** y dentro de este se deben crear los archivos de configuración. Ejemplo: ```json= -/tree/main/presupuesto/presupuesto-igrau.properties ``` 2. Crear el repositorio Git para almacenar los archivos de configuración de usuarios y constraseñas que utilizarán el spring config server. Cada linea que contenga el archivo de configuración de usuarios deber tener el siguiente formato ```json= username:password ``` > El algoritmo de encriptación utilizado es el Bcrypt 3. Generación de token de acceso personal para el usuario del Gitlab > [Ver: Run Spring config server](https://hackmd.io/1eb87bJ1RQyx7_SEsZStzw) ## Creación del proyecto En primer lugar, se debe crear los dos proyectos Maven. El proyecto del servidor depende ¿del módulo **spring-cloud-config-server**, así como de los paquetes de inicio **spring-boot-starter-security** y **spring-boot-starter-web**: ```xml= <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ``` Para el proyecto *cliente* es necesarios agregar las siguientes dependencias : ```xml= <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ``` ## Implementación del config server La parte principal de la aplicación es una clase de configuración, más concretamente una **@SpringBootApplication**, que recoge toda la configuración necesaria a través de la anotación **@EnableConfigServer**: Adicionalmente se agrega la anotación **EnableConfigurationProperties**, para la cargar los valores del archivo de configuración como una clase **Bean** ```java= @EnableConfigServer @SpringBootApplication(exclude = { SecurityAutoConfiguration.class }) @EnableConfigurationProperties(value = { ConfigProperties.class }) public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } //Método utilizado para precargar los registros de usuario //los usuarios, los mismos son guardados en memoria en una base h2 //y son accedidos por el Spring Security para la validación de acceso a los //recursos expuestos por el Spring config server @Bean @ConditionalOnProperty(prefix = "custom.config.server", name = "enabled", havingValue = "") public CommandLineRunner commandLineRunner(ApplicationContext ctx) { return args -> { UsuarioDetallesServiceImpl service = ctx.getBean(UsuarioDetallesServiceImpl.class); service.loadUsers(); }; } } ``` ### Creación de archivo de configuración Para el correcto funcionamiento del Spring config server, es necesario agregar la configuración del puerto y la url al repositorio Gitlab, que proporciona nuestro contenido de configuraciones controlado por ramas. También necesitamos establecer la configuración de un repositorio Git desde donde serán obtenidos los datos de usuarios que pueden acceder al server. Ejemplo : ```json server.port: ${PORT:8888} spring.application.name=config-server spring.cloud.config.server.git.uri= https://gitlab.siare.gov.py/siare-develop/spring-config-repo.git spring.cloud.config.server.git.skipSsValidation=true spring.cloud.config.server.git.username= configserver spring.cloud.config.server.git.password={contraseña} spring.cloud.config.server.git.default-label=main spring.cloud.config.server.git.force-pull=true spring.cloud.config.server.git.clone-on-start=true spring.cloud.config.server.git.search-paths={application} custom.config.server.url=https://gitlab.siare.gov.py/siare-develop/spring-config-server/-/raw/feature-add-spring-security/configuraciones/usuarios.pas custom.config.server.token= ``` * Los datos importantes a cambiar de ser necesario son : * uri : url del repositorio git donde estan todas las configuraciones por perfil de usuarios * username : alias de usuario para el acceso del git * password : contraseña asociada al usuario del git * server.url : url del repositorio git donde se encuentra el listado de usuario y contraseña para el acceso al spring cloud server * server.token : token de acceso asigando al usuario que tiene el **rol de Reporter** en el repositorio git, esto permite descargar el listado de usuarios y contraseñas del git :::info Observación : Estas configuraciones se pueden asignar en el momento de la ejecución del proyecto. Los datos son de ejempl, es necasario cambiarlo. ::: ### Implementación del spring security La implementación del spring security consta de los siguientes archivos principales * SecurityConfig.java El archivo es una clase de configuración para habilitar la funcionalidad del spring security. En ella se configura el servicio de obtención de usuarios y el método de encriptación a ser utilizados por el **AuthenticationManager**. Así también la regla de filtros a utilizar para el acceso a recursos expuestos. ```java= @Configuration @EnableWebSecurity @AllArgsConstructor @ConditionalOnProperty(prefix = "custom.config.server", name = "enabled", havingValue = "") public class SecurityConfig extends WebSecurityConfigurerAdapter { private UserDetailsService userDetails; private PasswordEncoder passwordEncoder; @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin().disable().csrf().disable() .addFilterAt(new AuthCustomFilter(authenticationManager()), BasicAuthenticationFilter.class) .authorizeRequests().mvcMatchers("/**").authenticated().and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); } @Override @Bean protected AuthenticationManager authenticationManager() throws Exception { return super.authenticationManager(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetails).passwordEncoder(passwordEncoder); } } ``` * AuthCustomFilter.java El archivo es una clase que hereda comportamientos del Filtro *OncePerRequestFilter*, que permite interceptar las peticiones HTTP entrantes y tratarlos. En ella se verifica la existencia de usuario y contraseña en la cabecera HTTP de la petición realizada y delega la verificación del mismo al **AuthenticationManager**. ```java= public class AuthCustomFilter extends OncePerRequestFilter { private AuthenticationManager authenticationManager; public AuthCustomFilter(AuthenticationManager authenticationManager) { super(); this.authenticationManager = authenticationManager; } private BasicAuthenticationConverter authenticationConverter = new BasicAuthenticationConverter(); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { UsernamePasswordAuthenticationToken authRequest = this.authenticationConverter.convert(request); if (authRequest == null) { this.logger.trace("Did not process authentication request since failed to find " + "username and password in Basic Authorization header"); filterChain.doFilter(request, response); return; } String username = authRequest.getName(); Object password = authRequest.getCredentials(); if (Objects.nonNull(username) && Objects.nonNull(password)) { Authentication authResult = this.authenticationManager.authenticate(authRequest); if (authResult.isAuthenticated()) { SecurityContextHolder.getContext().setAuthentication(authResult); filterChain.doFilter(request, response); } else { this.logger.debug("Did not process authentication request since failed to find " + "username and password in Basic Authorization header"); filterChain.doFilter(request, response); return; } } } catch (AuthenticationException ex) { SecurityContextHolder.clearContext(); this.logger.debug("Failed to process authentication request", ex); return; } filterChain.doFilter(request, response); } } ``` * ConfigServer.java El archivo es una clase de configuración para la creación del tipo de encriptación a ser utilizado por el **AuthenticationManager**. ```java= @Configuration public class ConfigServer { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } } ``` ## Implementación del config server client Para poder consumir los servicios expuestos por el config server es necesario realizar las siguientes configuraciones : 1. Agregar al pom.xml del proyecto consumidor: > 1.1. Dependencia con el spring-cloud-starter-config y spring-boot-starter-web: ```xml= <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ``` > 1.2. versión de spring-cloud en properties: ```xml= <properties> ... <spring-cloud.version>2020.0.4</spring-cloud.version> </properties> ``` > 1.3. manejo de las depencencias de spring cloud ```xml= <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ``` > 1.4 Goals de ejecución para el plugin de spring-boot ```xml <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> <goal>build-info</goal> </goals> </execution> </executions> <configuration> ..... </configuration> </plugin> ``` 2. Agregar en el archivo **application.properties** de la aplicación cliente creada las configuraciones necesarias para la conexión con el *spring cloud config server*. Ejemplo de configuración: ```json # Application Name spring.application.name=presupuesto # Spring Cloud Config Server Repository spring.config.import=configserver:http://db-dev.siare.gov.py:8888/ spring.cloud.config.uri= http://db-dev.siare.gov.py:8888/ spring.cloud.config.fail-fast=true spring.profiles.active=local spring.cloud.config.username=dev spring.cloud.config.password=12345 ``` 3. Configurar el despliegue en entorno local agregando el nombre del perfil correspondiente (Ver Figura 2 y 3). ![](https://i.imgur.com/vz7IOrR.png) Figura 2. Despliegue ![](https://i.imgur.com/2eut1CC.png) Figura 3. Configuración > En caso que el despliegue falle, primero es necesario ejecutar la operación de *maven install* antes de volver a desplegar la aplicación. * Otra forma de ejecutar el proyecto es desde la terminal de comandos, situarse en la carpeta ...\siare-back\presupuesto del proyecto y ejecutar la instrucción ```shell= java -server -jar /target/presupuesto-1.0.3.jar --spring.profile.active=<nombre perfil> ``` # Recursos * [using-spring-config-server](https://dzone.com/articles/using-spring-config-server) * [Generador de contraseñas utilizando Bcrypt](https://bcrypt-generator.com/). * [Formato del archivo de usuarios y contraseñas](https://httpd.apache.org/docs/2.4/programs/htpasswd.html) * [Configuración del archivo .properties](https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html)