I can reach my Config Server at http://localhost:8888, but I can’t load the desired configurations from my location-service.yml file.
My Configuration Server (CNF-S)
application.yml:
# Basic Configuration - Spring Config Server (CNF-S)
spring:
application:
name: Configuration
profiles:
active:
- ${ENVIRONMENT} # Which is "dev" currently
default: dev
# GitHub Repository (private & SSH)
cloud:
config:
server:
git:
uri: [email protected]:*****/EMP_CNF-S.git # Private git repo
ignore-local-ssh-settings: true
default-label: ${ENVIRONMENT}
private-key: ${GIT_SSH_KEY}
search-paths: src/main/resources
skip-ssl-validation: true
timeout: 10
clone-on-start: true
# Spring Security
security:
user:
name: ${EMP_CONFIG_USERNAME}
password: ${EMP_CONFIG_PASSWORD}
# Server port
server:
port: 8888
location-service.yml on Config Server (CNF-S):
spring:
datasource:
url: jdbc:mysql://localhost:3306/location_db
username: root
password: test-123!
jpa:
hibernate:
ddl-auto: update
show-sql: true
test:
database:
replace: none
My Client - Location Service (LOC-S)
bootstrap.yml:
# Basic configs - Name & Profile
spring:
application:
name: location-service
profiles:
active:
- ${ENVIRONMENT}
# Connecting to CNF-S - Configuration Server
config:
import: optional:configserver:${BASE_URL}:8888
cloud:
config:
label: ${ENVIRONMENT}
uri: ${BASE_URL}:8888
name: location-service
username: ${EMP_CONFIG_USERNAME}
password: ${EMP_CONFIG_PASSWORD}
# Configuring Port for Location-Service
server:
port: 8080
# Spring Actuator
management:
endpoints:
web:
exposure:
include:
- health
- info
base-path: /actuator
endpoint:
health:
show-details: always
Issue Description
I am able to log into my Config Server and see my configurations at http://localhost:8888/location-service/dev. However, when my client tries to fetch the configurations, I receive the following error:
2024-09-23T15:38:53.706+02:00 INFO 43435 --- [location-service] [ restartedMain] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://localhost:8888
2024-09-23T15:38:53.712+02:00 WARN 43435 --- [location-service] [ restartedMain] c.c.c.ConfigServicePropertySourceLocator : Could not locate PropertySource: Could not extract response: no suitable HttpMessageConverter found for response type [class org.springframework.cloud.config.environment.Environment] and content type [text/html;charset=UTF-8]
2024-09-23T15:38:53.714+02:00 INFO 43435 --- [location-service] [ restartedMain] com.emp.loc.LocationApplication : The following 1 profile is active: "dev"
2024-09-23T15:38:53.719+02:00 INFO 43435 --- [location-service] [ restartedMain] o.s.c.c.c.ConfigServerConfigDataLoader : Fetching config from server at : http://localhost:8888
2024-09-23T15:38:53.719+02:00 WARN 43435 --- [location-service] [ restartedMain] o.s.c.c.c.ConfigServerConfigDataLoader : Could not locate PropertySource ([ConfigServerConfigDataResource@265110bb uris = array<String>['http://localhost:8888'], optional = true, profiles = 'default']): Could not extract response: no suitable HttpMessageConverter found for response type [class org.springframework.cloud.config.environment.Environment] and content type [text/html;charset=UTF-8]
2024-09-23T15:38:53.719+02:00 INFO 43435 --- [location-service] [ restartedMain] o.s.c.c.c.ConfigServerConfigDataLoader : Fetching config from server at : http://localhost:8888
2024-09-23T15:38:53.719+02:00 WARN 43435 --- [location-service] [ restartedMain] o.s.c.c.c.ConfigServerConfigDataLoader : Could not locate PropertySource ([ConfigServerConfigDataResource@3373a584 uris = array<String>['http://localhost:8888'], optional = true, profiles = 'dev']): Could not extract response: no suitable HttpMessageConverter found for response type [class org.springframework.cloud.config.environment.Environment] and content type [text/html;charset=UTF-8]
2024-09-23T15:38:53.719+02:00 INFO 43435 --- [location-service] [ restartedMain] o.s.c.c.c.ConfigServerConfigDataLoader : Fetching config from server at : http://localhost:8888
2024-09-23T15:38:53.719+02:00 WARN 43435 --- [location-service] [ restartedMain] o.s.c.c.c.ConfigServerConfigDataLoader : Could not locate PropertySource ([ConfigServerConfigDataResource@1d09bf99 uris = array<String>['http://localhost:8888'], optional = true, profiles = 'default']): Could not extract response: no suitable HttpMessageConverter found for response type [class org.springframework.cloud.config.environment.Environment] and content type [text/html;charset=UTF-8]
2024-09-23T15:38:54.010+02:00 INFO 43435 --- [location-service] [ restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2024-09-23T15:38:54.016+02:00 INFO 43435 --- [location-service] [ restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 2 ms. Found 0 JPA repository interfaces.
2024-09-23T15:38:54.077+02:00 INFO 43435 --- [location-service] [ restartedMain] o.s.cloud.context.scope.GenericScope : BeanFactory id=845d36ed-6367-395b-a199-708537ceebfe
2024-09-23T15:38:54.392+02:00 INFO 43435 --- [location-service] [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2024-09-23T15:38:54.400+02:00 INFO 43435 --- [location-service] [ restartedMain] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2024-09-23T15:38:54.400+02:00 INFO 43435 --- [location-service] [ restartedMain] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.28]
2024-09-23T15:38:54.417+02:00 INFO 43435 --- [location-service] [ restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2024-09-23T15:38:54.418+02:00 INFO 43435 --- [location-service] [ restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 698 ms
2024-09-23T15:38:54.494+02:00 WARN 43435 --- [location-service] [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Failed to initialize dependency 'dataSourceScriptDatabaseInitializer' of LoadTimeWeaverAware bean 'entityManagerFactory': Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: Unsatisfied dependency expressed through method 'dataSourceScriptDatabaseInitializer' parameter 0: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception with message: Failed to determine a suitable driver class
2024-09-23T15:38:54.495+02:00 INFO 43435 --- [location-service] [ restartedMain] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2024-09-23T15:38:54.511+02:00 INFO 43435 --- [location-service] [ restartedMain] .s.b.a.l.ConditionEvaluationReportLogger :
Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled.
2024-09-23T15:38:54.518+02:00 ERROR 43435 --- [location-service] [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (the profiles dev are currently active).
Process finished with exit code 0
PS: The value of ${EMP_CONFIG_PASSWORD} is the hashed value of my plaintext password => $2a$12$a.2zjPN5uKwAfi*********
My Config Server has a SecurityConfiguration.java class which looks like this:
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
// Loading environment variables - Username & Password
@Value("${EMP_CONFIG_USERNAME}")
private String envUsername;
@Value("${EMP_CONFIG_PASSWORD}")
private String envPassword;
// Password Encoder
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
// SecurityFilterChain
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
// Checking if username and password are available
boolean userInMemoryAuth = !envUsername.isEmpty() && !envPassword.isEmpty();
// Security setup based on environment
http
.csrf(csrf -> csrf.disable())
.cors(cors -> cors.configurationSource(request -> {
var corsConfig = new CorsConfiguration();
corsConfig.addAllowedOrigin("http://localhost:8888");
corsConfig.addAllowedHeader("*");
corsConfig.addAllowedMethod("*");
corsConfig.setAllowCredentials(true);
return corsConfig;
}));
if (userInMemoryAuth){
http.authorizeHttpRequests(authorizeRequest ->
authorizeRequest.anyRequest().authenticated())
.formLogin(withDefaults());
}else{
http.authorizeHttpRequests(authorizeRequests ->
authorizeRequests.anyRequest().permitAll());
// TODO: Add JWT Authentication setup between Gateway Service and Configuration Service
}
return http.build();
}
// Authentication Provider - UserDetailService (InMemory)
@Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager(){
if(!envUsername.isEmpty() && !envPassword.isEmpty()){
UserDetails user = User
.withUsername(envUsername)
.password(envPassword)
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
return new InMemoryUserDetailsManager();
}
}
I could manage to solve the issue. In my SecurityConfiguration.java I've changed the "http.formLogin" -> "http.htpBasic". Then it worked.
The root cause is that since my Spring Security enables a form login which can't be submitted by http post requests from postman or the Spring Config Client itself.
Now with this setup for Spring Security it worked fine: SecurityConfiguration.java:
package com.emp.config.security;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import static org.springframework.security.config.Customizer.withDefaults;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
// Loading environment variables - Username & Password
@Value("${EMP_CONFIG_USERNAME}")
private String envUsername;
@Value("${EMP_CONFIG_PASSWORD}")
private String envPassword;
// Password Encoder
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
// SecurityFilterChain
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
// Checking if username and password are available
boolean userInMemoryAuth = !envUsername.isEmpty() && !envPassword.isEmpty();
// Security setup based on environment
http
.csrf(csrf -> csrf.disable())
.cors(cors -> cors.configurationSource(request -> {
var corsConfig = new CorsConfiguration();
corsConfig.addAllowedOrigin("http://localhost:8888");
corsConfig.addAllowedOrigin("http://localhost:8080");
corsConfig.addAllowedHeader("*");
corsConfig.addAllowedMethod("*");
corsConfig.setAllowCredentials(true);
return corsConfig;
}));
if (userInMemoryAuth){
http.authorizeHttpRequests(authorizeRequest ->
authorizeRequest.anyRequest().authenticated())
.httpBasic(withDefaults());
}else{
http.authorizeHttpRequests(authorizeRequests ->
authorizeRequests.anyRequest().permitAll());
// TODO: Add JWT Authentication setup between Gateway Service and Configuration Service
}
return http.build();
}
// Authentication Provider - UserDetailService (InMemory)
@Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager(){
if(!envUsername.isEmpty() && !envPassword.isEmpty()){
UserDetails user = User
.withUsername(envUsername)
.password(envPassword) // Encode the password here
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
return new InMemoryUserDetailsManager();
}
}