微服务基础模块: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.json.jackson.JacksonJsonpMapper;
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.ESDocumentServiceImpl;
import lombok.extern.slf4j.Slf4j;
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.RestClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
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
@@ -21,20 +47,19 @@ import org.springframework.util.StringUtils;
@AutoConfiguration
@ConditionalOnClass(ElasticsearchClient.class)
@EnableConfigurationProperties(EsProperties.class)
@Slf4j
public class ChenfengElasticsearchAutoConfiguration {
//超时时间设置
public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 10000;
public static final int DEFAULT_SOCKET_TIMEOUT_MILLIS = 300000;
public static final int DEFAULT_CONNECT_REQUEST_TIMEOUT_MILLIS = 1000;
@Value("${chenfeng.encrypt.publicKey:}")
private String publicKey;
/**
* 同步方式
*
*/
@Bean
public ElasticsearchClient elasticsearchClient(EsProperties properties) {
return new ElasticsearchClient(getTransport(properties.getUris()));
public ElasticsearchClient elasticsearchClient(RestClientTransport transport) {
return new ElasticsearchClient(transport);
}
/**
@@ -42,44 +67,110 @@ public class ChenfengElasticsearchAutoConfiguration {
*
*/
@Bean
public ElasticsearchAsyncClient elasticsearchAsyncClient(EsProperties properties) {
return new ElasticsearchAsyncClient(getTransport(properties.getUris()));
public ElasticsearchAsyncClient elasticsearchAsyncClient(RestClientTransport transport) {
return new ElasticsearchAsyncClient(transport);
}
@Bean
private ESDocumentService esDocumentService(ElasticsearchClient elasticsearchClient, ElasticsearchAsyncClient elasticsearchAsyncClient) {
public ESDocumentService esDocumentService(ElasticsearchClient elasticsearchClient, ElasticsearchAsyncClient elasticsearchAsyncClient) {
return new ESDocumentServiceImpl(elasticsearchClient, elasticsearchAsyncClient);
}
/**
* 获取客户端 RestClientTransport
*/
private RestClientTransport getTransport(String hosts){
HttpHost[] httpHosts = toHttpHost(hosts);
RestClient restClient = getRestClient(httpHosts);
return new RestClientTransport(restClient, new JacksonJsonpMapper());
@Bean
public RestClientTransport getTransport(RestClient client){
return new RestClientTransport(client, new JacksonJsonpMapper());
}
/**
* 获取客户端RestClient
* @param httpHosts http数组
* chenfeng.encrypt.enabletrue
*
* @param properties es配置
*/
private RestClient getRestClient(HttpHost[] httpHosts){
return RestClient.builder(httpHosts).setRequestConfigCallback(requestConfigBuilder -> {
requestConfigBuilder.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS);
requestConfigBuilder.setSocketTimeout(DEFAULT_SOCKET_TIMEOUT_MILLIS);
requestConfigBuilder.setConnectionRequestTimeout(DEFAULT_CONNECT_REQUEST_TIMEOUT_MILLIS);
@Bean
@ConditionalOnProperty(name = "chenfeng.encrypt.enable", havingValue = "true")
public RestClient getAuthRestClient(EsProperties properties) {
// 配置账号密码
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;
}).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)) {
throw new RuntimeException("invalid elasticsearch configuration. elasticsearch.hosts不能为空!");
throw new IllegalArgumentException("invalid elasticsearch configuration. elasticsearch.hosts不能为空!");
}
// 多个IP逗号隔开
String[] hostArray = hosts.split(",");
@@ -87,14 +178,9 @@ public class ChenfengElasticsearchAutoConfiguration {
HttpHost httpHost;
for (int i = 0; i < hostArray.length; i++) {
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;
}
return httpHosts;
}
}
@@ -2,12 +2,46 @@ package com.cf.imes.framework.es.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.time.Duration;
/**
* es环境配置
* @author there
*/
@ConfigurationProperties(prefix = "spring.elasticsearch")
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() {
return uris;
}
@@ -16,5 +50,59 @@ public class EsProperties {
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
@AutoConfigureBefore(DynamicDataSourceAutoConfiguration.class)
@ConditionalOnProperty(name = "chenfeng.encrypt.enable", havingValue = "true")
public class ChenfengDataSourceEncryptConfiguration {
@Bean
@ConditionalOnProperty(name = "chenfeng.encrypt.enable", havingValue = "true")
public DataSourceInitEvent getDataSourceInitEvent() {
return new ChenfengDataSourceEncryptInitEvent();
}
@@ -20,13 +20,13 @@ import org.springframework.util.StringUtils;
* @since 2024/8/27 9:46
*/
@AutoConfiguration
@ConditionalOnProperty(name = "chenfeng.encrypt.enable", havingValue = "true")
public class ChenfengRedisEncryptAutoConfiguration {
@Value("${chenfeng.encrypt.publicKey:}")
private String publicKey;
@Bean
@ConditionalOnProperty(name = "chenfeng.encrypt.enable", havingValue = "true")
public RedissonAutoConfigurationCustomizer redissonAutoConfigurationCustomizer() {
return configuration -> {
Config redissonConfig = new Config();