微服务基础模块:1、数据库、redis账号密码解密配置类注入方式修改;2、elasticsearch支持账号密码解密、支持https;

This commit is contained in:
gaoqr
2024-09-04 16:05:20 +08:00
parent 5264991a3c
commit 7cac04e95e
8 changed files with 217 additions and 35 deletions
@@ -4,16 +4,42 @@ import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
import co.elastic.clients.elasticsearch.ElasticsearchClient; import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper; import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.rest_client.RestClientTransport; import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.cf.imes.framework.common.util.encrypt.AesUtils;
import com.cf.imes.framework.es.core.service.ESDocumentService; import com.cf.imes.framework.es.core.service.ESDocumentService;
import com.cf.imes.framework.es.core.service.ESDocumentServiceImpl; import com.cf.imes.framework.es.core.service.ESDocumentServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost; import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
/** /**
* @author there * @author there
@@ -21,20 +47,19 @@ import org.springframework.util.StringUtils;
@AutoConfiguration @AutoConfiguration
@ConditionalOnClass(ElasticsearchClient.class) @ConditionalOnClass(ElasticsearchClient.class)
@EnableConfigurationProperties(EsProperties.class) @EnableConfigurationProperties(EsProperties.class)
@Slf4j
public class ChenfengElasticsearchAutoConfiguration { public class ChenfengElasticsearchAutoConfiguration {
//超时时间设置 @Value("${chenfeng.encrypt.publicKey:}")
public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 10000; private String publicKey;
public static final int DEFAULT_SOCKET_TIMEOUT_MILLIS = 300000;
public static final int DEFAULT_CONNECT_REQUEST_TIMEOUT_MILLIS = 1000;
/** /**
* 同步方式 * 同步方式
* *
*/ */
@Bean @Bean
public ElasticsearchClient elasticsearchClient(EsProperties properties) { public ElasticsearchClient elasticsearchClient(RestClientTransport transport) {
return new ElasticsearchClient(getTransport(properties.getUris())); return new ElasticsearchClient(transport);
} }
/** /**
@@ -42,44 +67,110 @@ public class ChenfengElasticsearchAutoConfiguration {
* *
*/ */
@Bean @Bean
public ElasticsearchAsyncClient elasticsearchAsyncClient(EsProperties properties) { public ElasticsearchAsyncClient elasticsearchAsyncClient(RestClientTransport transport) {
return new ElasticsearchAsyncClient(getTransport(properties.getUris())); return new ElasticsearchAsyncClient(transport);
} }
@Bean @Bean
private ESDocumentService esDocumentService(ElasticsearchClient elasticsearchClient, ElasticsearchAsyncClient elasticsearchAsyncClient) { public ESDocumentService esDocumentService(ElasticsearchClient elasticsearchClient, ElasticsearchAsyncClient elasticsearchAsyncClient) {
return new ESDocumentServiceImpl(elasticsearchClient, elasticsearchAsyncClient); return new ESDocumentServiceImpl(elasticsearchClient, elasticsearchAsyncClient);
} }
/** /**
* 获取客户端 RestClientTransport * 获取客户端 RestClientTransport
*/ */
private RestClientTransport getTransport(String hosts){ @Bean
HttpHost[] httpHosts = toHttpHost(hosts); public RestClientTransport getTransport(RestClient client){
RestClient restClient = getRestClient(httpHosts); return new RestClientTransport(client, new JacksonJsonpMapper());
return new RestClientTransport(restClient, new JacksonJsonpMapper());
} }
/** /**
* 获取客户端RestClient * 获取客户端RestClient
* @param httpHosts http数组 * chenfeng.encrypt.enabletrue
*
* @param properties es配置
*/ */
private RestClient getRestClient(HttpHost[] httpHosts){ @Bean
return RestClient.builder(httpHosts).setRequestConfigCallback(requestConfigBuilder -> { @ConditionalOnProperty(name = "chenfeng.encrypt.enable", havingValue = "true")
requestConfigBuilder.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS); public RestClient getAuthRestClient(EsProperties properties) {
requestConfigBuilder.setSocketTimeout(DEFAULT_SOCKET_TIMEOUT_MILLIS); // 配置账号密码
requestConfigBuilder.setConnectionRequestTimeout(DEFAULT_CONNECT_REQUEST_TIMEOUT_MILLIS); final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(AesUtils.decrypt(properties.getUsername(), publicKey), AesUtils.decrypt(properties.getPassword(), publicKey)));
RestClientBuilder.HttpClientConfigCallback httpClientConfigCallback = httpClientBuilder -> {
// 设置账号密码、连接信息
HttpAsyncClientBuilder httpAsyncClientBuilder = httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(properties.getConnectionTimeout().toSecondsPart())
.setSocketTimeout(properties.getSocketTimeout().toSecondsPart())
.setConnectionRequestTimeout(properties.getConnectionRequestTimeout().toSecondsPart())
.build());
if (properties.isSecurityHttpSslEnable()) {
// 开启ssl配置连接证书
httpAsyncClientBuilder.setSSLContext(buildSSLContext()).setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
}
return httpAsyncClientBuilder;
};
return RestClient.builder(toHttpHost(properties.getUris(), properties.isSecurityHttpSslEnable())).setHttpClientConfigCallback(httpClientConfigCallback).build();
}
/**
* 获取客户端RestClient(默认)
* chenfeng.encrypt.enablefalse或者缺省
*
* @param properties es配置
*/
@Bean
@ConditionalOnProperty(name = "chenfeng.encrypt.enable", havingValue = "false", matchIfMissing = true)
public RestClient getRestClient(EsProperties properties) {
return RestClient.builder(toHttpHost(properties.getUris(), properties.isSecurityHttpSslEnable())).setRequestConfigCallback(requestConfigBuilder -> {
requestConfigBuilder.setConnectTimeout(properties.getConnectionTimeout().toSecondsPart());
requestConfigBuilder.setSocketTimeout(properties.getSocketTimeout().toSecondsPart());
requestConfigBuilder.setConnectionRequestTimeout(properties.getConnectionRequestTimeout().toSecondsPart());
return requestConfigBuilder; return requestConfigBuilder;
}).build(); }).build();
} }
/** /**
* 解析配置的字符串hosts,转为HttpHost对象数组 * 构建ssl请求信息
*
* @return
*/ */
private HttpHost[] toHttpHost(String hosts) { private SSLContext buildSSLContext() {
SSLContext sslContext = null;
try {
ClassPathResource resource = new ClassPathResource("http_ca.crt");
CertificateFactory factory = CertificateFactory.getInstance("X.509");
Certificate trustedCa;
try (InputStream is = resource.getInputStream()) {
trustedCa = factory.generateCertificate(is);
}
KeyStore trustStore = KeyStore.getInstance("pkcs12");
trustStore.load(null, null);
trustStore.setCertificateEntry("ca", trustedCa);
SSLContextBuilder sslContextBuilder = SSLContexts.custom()
.loadTrustMaterial(trustStore, null);
sslContext = sslContextBuilder.build();
} catch (CertificateException | IOException | KeyStoreException | NoSuchAlgorithmException |
KeyManagementException e) {
log.error("ES连接认证失败", e);
}
return sslContext;
}
/**
* 解析域名
*
* @param hosts 域名字符串
* @param isSslEnable 是否开启ssl
* @return
*/
private HttpHost[] toHttpHost(String hosts, boolean isSslEnable) {
if (!StringUtils.hasLength(hosts)) { if (!StringUtils.hasLength(hosts)) {
throw new RuntimeException("invalid elasticsearch configuration. elasticsearch.hosts不能为空!"); throw new IllegalArgumentException("invalid elasticsearch configuration. elasticsearch.hosts不能为空!");
} }
// 多个IP逗号隔开 // 多个IP逗号隔开
String[] hostArray = hosts.split(","); String[] hostArray = hosts.split(",");
@@ -87,14 +178,9 @@ public class ChenfengElasticsearchAutoConfiguration {
HttpHost httpHost; HttpHost httpHost;
for (int i = 0; i < hostArray.length; i++) { for (int i = 0; i < hostArray.length; i++) {
String[] strings = hostArray[i].split(":"); String[] strings = hostArray[i].split(":");
httpHost = new HttpHost(strings[0], Integer.parseInt(strings[1]), "http"); httpHost = new HttpHost(strings[0], Integer.parseInt(strings[1]), isSslEnable ? "https" : "http");
httpHosts[i] = httpHost; httpHosts[i] = httpHost;
} }
return httpHosts; return httpHosts;
} }
} }
@@ -2,12 +2,46 @@ package com.cf.imes.framework.es.config;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;
/** /**
* es环境配置 * es环境配置
* @author there * @author there
*/ */
@ConfigurationProperties(prefix = "spring.elasticsearch") @ConfigurationProperties(prefix = "spring.elasticsearch")
public class EsProperties { public class EsProperties {
private String uris;
/**
* Username for authentication with Elasticsearch.
*/
private String username;
/**
* Password for authentication with Elasticsearch.
*/
private String password;
/**
* Connection timeout used when communicating with Elasticsearch.
*/
private Duration connectionTimeout = Duration.ofSeconds(10);
/**
* Socket timeout used when communicating with Elasticsearch.
*/
private Duration socketTimeout = Duration.ofSeconds(30);
private Duration connectionRequestTimeout = Duration.ofSeconds(1);
/**
* Prefix added to the path of every request sent to Elasticsearch.
*/
private String pathPrefix;
private boolean securityHttpSslEnable = false;
public String getUris() { public String getUris() {
return uris; return uris;
} }
@@ -16,5 +50,59 @@ public class EsProperties {
this.uris = uris; this.uris = uris;
} }
private String uris; public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public Duration getConnectionTimeout() {
return this.connectionTimeout;
}
public void setConnectionTimeout(Duration connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}
public Duration getSocketTimeout() {
return this.socketTimeout;
}
public void setSocketTimeout(Duration socketTimeout) {
this.socketTimeout = socketTimeout;
}
public String getPathPrefix() {
return this.pathPrefix;
}
public void setPathPrefix(String pathPrefix) {
this.pathPrefix = pathPrefix;
}
public boolean isSecurityHttpSslEnable() {
return securityHttpSslEnable;
}
public void setSecurityHttpSslEnable(boolean securityHttpSslEnable) {
this.securityHttpSslEnable = securityHttpSslEnable;
}
public Duration getConnectionRequestTimeout() {
return connectionRequestTimeout;
}
public void setConnectionRequestTimeout(Duration connectionRequestTimeout) {
this.connectionRequestTimeout = connectionRequestTimeout;
}
} }
@@ -15,9 +15,9 @@ import org.springframework.context.annotation.Bean;
*/ */
@AutoConfiguration @AutoConfiguration
@AutoConfigureBefore(DynamicDataSourceAutoConfiguration.class) @AutoConfigureBefore(DynamicDataSourceAutoConfiguration.class)
@ConditionalOnProperty(name = "chenfeng.encrypt.enable", havingValue = "true")
public class ChenfengDataSourceEncryptConfiguration { public class ChenfengDataSourceEncryptConfiguration {
@Bean @Bean
@ConditionalOnProperty(name = "chenfeng.encrypt.enable", havingValue = "true")
public DataSourceInitEvent getDataSourceInitEvent() { public DataSourceInitEvent getDataSourceInitEvent() {
return new ChenfengDataSourceEncryptInitEvent(); return new ChenfengDataSourceEncryptInitEvent();
} }
@@ -20,13 +20,13 @@ import org.springframework.util.StringUtils;
* @since 2024/8/27 9:46 * @since 2024/8/27 9:46
*/ */
@AutoConfiguration @AutoConfiguration
@ConditionalOnProperty(name = "chenfeng.encrypt.enable", havingValue = "true")
public class ChenfengRedisEncryptAutoConfiguration { public class ChenfengRedisEncryptAutoConfiguration {
@Value("${chenfeng.encrypt.publicKey:}") @Value("${chenfeng.encrypt.publicKey:}")
private String publicKey; private String publicKey;
@Bean @Bean
@ConditionalOnProperty(name = "chenfeng.encrypt.enable", havingValue = "true")
public RedissonAutoConfigurationCustomizer redissonAutoConfigurationCustomizer() { public RedissonAutoConfigurationCustomizer redissonAutoConfigurationCustomizer() {
return configuration -> { return configuration -> {
Config redissonConfig = new Config(); Config redissonConfig = new Config();
@@ -157,5 +157,7 @@ chenfeng:
- infra_job_log - infra_job_log
- infra_job_log - infra_job_log
- infra_data_source_config - infra_data_source_config
encrypt:
enable: false
publicKey: cfimes
debug: false debug: false
@@ -161,5 +161,7 @@ chenfeng:
send-maximum-quantity-per-day: 10 send-maximum-quantity-per-day: 10
begin-code: 9999 # 这里配置 9999 的原因是,测试方便。 begin-code: 9999 # 这里配置 9999 的原因是,测试方便。
end-code: 9999 # 这里配置 9999 的原因是,测试方便。 end-code: 9999 # 这里配置 9999 的原因是,测试方便。
encrypt:
enable: false
publicKey: cfimes
debug: false debug: false
@@ -175,5 +175,7 @@ chenfeng:
send-maximum-quantity-per-day: 10 send-maximum-quantity-per-day: 10
begin-code: 9999 # 这里配置 9999 的原因是,测试方便。 begin-code: 9999 # 这里配置 9999 的原因是,测试方便。
end-code: 9999 # 这里配置 9999 的原因是,测试方便。 end-code: 9999 # 这里配置 9999 的原因是,测试方便。
encrypt:
enable: false
publicKey: cfimes
debug: false debug: false
@@ -103,6 +103,8 @@ chenfeng:
organ: # 多租户相关配置项 organ: # 多租户相关配置项
enable: true enable: true
ignore-tables: ignore-tables:
encrypt:
enable: false
publicKey: cfimes
debug: false debug: false