我可以通过 http://localhost:8888 访问我的配置服务器,但无法从 location-service.yml 文件加载所需的配置。
我的配置服务器(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
配置服务器 (CNF-S) 上的location-service.yml:
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
我的客户 - 定位服务 (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
问题描述
我可以登录我的配置服务器并在 http://localhost:8888/location-service/dev 上查看我的配置。但是,当我的客户尝试获取配置时,我收到以下错误:
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:${EMP_CONFIG_PASSWORD}的值是我的明文密码的哈希值 => $2a$12$a.2zjPN5uKwAfi************
我的配置服务器有一个 SecurityConfiguration.java 类,如下所示:
@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();
}
}
我可以设法解决这个问题。在我的 SecurityConfiguration.java 中,我更改了“http.formLogin”->“http.htpBasic”。然后就成功了。
根本原因是,由于我的 Spring Security 启用了表单登录,因此无法通过邮递员或 Spring Config Client 本身的 http post 请求提交。
现在有了 Spring Security 的这个设置,它工作得很好: 安全配置.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();
}
}