Add SSL support for sentinel-datasource-redis (#3045)

This commit is contained in:
langshiquan 2023-04-23 10:30:39 +08:00 committed by LearningGp
parent cc6923f078
commit 74f7d184cf
4 changed files with 322 additions and 17 deletions

View File

@ -15,7 +15,7 @@
<properties>
<java.source.version>1.8</java.source.version>
<java.target.version>1.8</java.target.version>
<lettuce.version>5.0.1.RELEASE</lettuce.version>
<lettuce.version>5.3.1.RELEASE</lettuce.version>
<redis.mock.version>0.1.6</redis.mock.version>
</properties>

View File

@ -25,7 +25,9 @@ import com.alibaba.csp.sentinel.util.StringUtil;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.SslOptions;
import io.lettuce.core.api.sync.RedisCommands;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
import io.lettuce.core.cluster.pubsub.StatefulRedisClusterPubSubConnection;
@ -33,10 +35,10 @@ import io.lettuce.core.pubsub.RedisPubSubAdapter;
import io.lettuce.core.pubsub.StatefulRedisPubSubConnection;
import io.lettuce.core.pubsub.api.sync.RedisPubSubCommands;
import java.io.File;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* <p>
@ -94,19 +96,70 @@ public class RedisDataSource<T> extends AbstractDataSource<String, T> {
subscribeFromChannel(channel);
}
/**
* init SslOptions, support jks or pem format
*
* @param connectionConfig Redis connection config
* @return a new SslOptions
*/
private SslOptions initSslOptions(RedisConnectionConfig connectionConfig) {
if (!connectionConfig.isSslEnable()){
return null;
}
SslOptions.Builder sslOptionsBuilder = SslOptions.builder();
if (connectionConfig.getTrustedCertificatesPath() != null){
if (connectionConfig.getTrustedCertificatesPath().endsWith(".jks")){
// if the value is end with .jksthink it is java key store formatto invoke truststore method
sslOptionsBuilder.truststore(
new File(connectionConfig.getTrustedCertificatesPath()),
connectionConfig.getTrustedCertificatesJksPassword()
);
} else {
// if the value is not end with .jksthink it is pem formatto invoke trustManager method
sslOptionsBuilder.trustManager(new File(connectionConfig.getTrustedCertificatesPath()));
}
}
if (connectionConfig.getKeyCertChainFilePath() != null || connectionConfig.getKeyFilePath() != null) {
if (connectionConfig.getKeyFilePath().endsWith(".jks")){
sslOptionsBuilder.keystore(
new File(connectionConfig.getKeyCertChainFilePath()),
connectionConfig.getKeyFilePassword() == null ? null : connectionConfig.getKeyFilePassword().toCharArray()
);
} else {
sslOptionsBuilder.keyManager(
new File(connectionConfig.getKeyCertChainFilePath()),
new File(connectionConfig.getKeyFilePath()),
connectionConfig.getKeyFilePassword() == null ? null : connectionConfig.getKeyFilePassword().toCharArray()
);
}
}
return sslOptionsBuilder.build();
}
/**
* Build Redis client fromm {@code RedisConnectionConfig}.
*
* @return a new {@link RedisClient}
*/
private RedisClient getRedisClient(RedisConnectionConfig connectionConfig) {
RedisClient redisClient;
if (connectionConfig.getRedisSentinels().size() == 0) {
RecordLog.info("[RedisDataSource] Creating stand-alone mode Redis client");
return getRedisStandaloneClient(connectionConfig);
redisClient = getRedisStandaloneClient(connectionConfig);
} else {
RecordLog.info("[RedisDataSource] Creating Redis Sentinel mode Redis client");
return getRedisSentinelClient(connectionConfig);
redisClient = getRedisSentinelClient(connectionConfig);
}
SslOptions sslOptions = initSslOptions(connectionConfig);
if (sslOptions != null){
redisClient.setOptions(
ClusterClientOptions.builder().sslOptions(sslOptions).build()
);
}
return redisClient;
}
private RedisClusterClient getRedisClusterClient(RedisConnectionConfig connectionConfig) {
@ -119,6 +172,7 @@ public class RedisDataSource<T> extends AbstractDataSource<String, T> {
RedisURI.Builder clusterRedisUriBuilder = RedisURI.builder();
clusterRedisUriBuilder.withHost(config.getHost())
.withPort(config.getPort())
.withSsl(config.isSslEnable())
.withTimeout(Duration.ofMillis(connectionConfig.getTimeout()));
//All redis nodes must have same password
if (password != null) {
@ -126,8 +180,16 @@ public class RedisDataSource<T> extends AbstractDataSource<String, T> {
}
redisUris.add(clusterRedisUriBuilder.build());
}
return RedisClusterClient.create(redisUris);
RedisClusterClient redisClusterClient = RedisClusterClient.create(redisUris);
SslOptions sslOptions = initSslOptions(connectionConfig);
if (sslOptions != null){
redisClusterClient.setOptions(
ClusterClientOptions.builder().sslOptions(sslOptions).build()
);
}
return redisClusterClient;
}
private RedisClient getRedisStandaloneClient(RedisConnectionConfig connectionConfig) {
char[] password = connectionConfig.getPassword();
@ -136,6 +198,7 @@ public class RedisDataSource<T> extends AbstractDataSource<String, T> {
redisUriBuilder.withHost(connectionConfig.getHost())
.withPort(connectionConfig.getPort())
.withDatabase(connectionConfig.getDatabase())
.withSsl(connectionConfig.isSslEnable())
.withTimeout(Duration.ofMillis(connectionConfig.getTimeout()));
if (password != null) {
redisUriBuilder.withPassword(connectionConfig.getPassword());
@ -160,6 +223,7 @@ public class RedisDataSource<T> extends AbstractDataSource<String, T> {
sentinelRedisUriBuilder.withClientName(clientName);
}
sentinelRedisUriBuilder.withSentinelMasterId(connectionConfig.getRedisSentinelMasterId())
.withSsl(connectionConfig.isSslEnable())
.withTimeout(Duration.ofMillis(connectionConfig.getTimeout()));
return RedisClient.create(sentinelRedisUriBuilder.build());
}

View File

@ -51,6 +51,12 @@ public class RedisConnectionConfig {
private String host;
private String redisSentinelMasterId;
private int port;
private boolean sslEnable;
private String trustedCertificatesPath;
private String trustedCertificatesJksPassword;
private String keyCertChainFilePath;
private String keyFilePath;
private String keyFilePassword;
private int database;
private String clientName;
private char[] password;
@ -329,6 +335,12 @@ public class RedisConnectionConfig {
private int database;
private String clientName;
private char[] password;
private boolean sslEnable;
private String trustedCertificatesPath;
private String trustedCertificatesJksPassword;
private String keyCertChainFilePath;
private String keyFilePath;
private String keyFilePassword;
private long timeout = DEFAULT_TIMEOUT_MILLISECONDS;
private final List<RedisHostAndPort> redisSentinels = new ArrayList<RedisHostAndPort>();
private final List<RedisHostAndPort> redisClusters = new ArrayList<RedisHostAndPort>();
@ -563,6 +575,7 @@ public class RedisConnectionConfig {
return this;
}
/**
* Configures authentication.
*
@ -619,6 +632,75 @@ public class RedisConnectionConfig {
return this;
}
/**
* Sets the sslEnable.
*
* @param sslEnable sslEnable
* @return the value of Builder
*/
public RedisConnectionConfig.Builder withSslEnable(boolean sslEnable) {
this.sslEnable = sslEnable;
return this;
}
/**
* Sets the trustedCertificatesPath.
*
* @param trustedCertificatesPath trustedCertificatesPath
* @return the value of Builder
*/
public RedisConnectionConfig.Builder withTrustedCertificatesPath(String trustedCertificatesPath) {
AssertUtil.notEmpty(trustedCertificatesPath, "trusted certificates path must not empty");
this.trustedCertificatesPath = trustedCertificatesPath;
return this;
}
/**
* Sets the trustedCertificatesJksPassword.
*
* @param trustedCertificatesJksPassword trustedCertificatesJksPassword
* @return the value of Builder
*/
public RedisConnectionConfig.Builder withTrustedCertificatesJksPassword(String trustedCertificatesJksPassword) {
this.trustedCertificatesJksPassword = trustedCertificatesJksPassword;
return this;
}
/**
* Sets the keyCertChainFilePath.
*
* @param keyCertChainFilePath keyCertChainFilePath
* @return the value of Builder
*/
public RedisConnectionConfig.Builder withKeyCertChainFilePath(String keyCertChainFilePath) {
this.keyCertChainFilePath = keyCertChainFilePath;
return this;
}
/**
* Sets the keyFilePath.
*
* @param keyFilePath keyFilePath
* @return the value of Builder
*/
public RedisConnectionConfig.Builder withKeyFilePath(String keyFilePath) {
this.keyFilePath = keyFilePath;
return this;
}
/**
* Sets the keyFilePassword.
*
* @param keyFilePassword keyFilePassword
* @return the value of Builder
*/
public RedisConnectionConfig.Builder withKeyFilePassword(String keyFilePassword) {
this.keyFilePassword = keyFilePassword;
return this;
}
/**
* @return the RedisConnectionConfig.
*/
@ -630,32 +712,41 @@ public class RedisConnectionConfig {
+ "Sentinel");
}
RedisConnectionConfig redisURI = new RedisConnectionConfig();
redisURI.setHost(host);
redisURI.setPort(port);
RedisConnectionConfig redisConnectionConfig = new RedisConnectionConfig();
redisConnectionConfig.setHost(host);
redisConnectionConfig.setPort(port);
if (password != null) {
redisURI.setPassword(password);
if (sslEnable){
redisConnectionConfig.setSslEnable(true);
redisConnectionConfig.setTrustedCertificatesPath(trustedCertificatesPath);
redisConnectionConfig.setTrustedCertificatesJksPassword(trustedCertificatesJksPassword);
redisConnectionConfig.setKeyCertChainFilePath(keyCertChainFilePath);
redisConnectionConfig.setKeyFilePath(keyFilePath);
redisConnectionConfig.setKeyFilePassword(keyFilePassword);
}
redisURI.setDatabase(database);
redisURI.setClientName(clientName);
if (password != null) {
redisConnectionConfig.setPassword(password);
}
redisURI.setRedisSentinelMasterId(redisSentinelMasterId);
redisConnectionConfig.setDatabase(database);
redisConnectionConfig.setClientName(clientName);
redisConnectionConfig.setRedisSentinelMasterId(redisSentinelMasterId);
for (RedisHostAndPort sentinel : redisSentinels) {
redisURI.getRedisSentinels().add(
redisConnectionConfig.getRedisSentinels().add(
new RedisConnectionConfig(sentinel.getHost(), sentinel.getPort(), timeout));
}
for (RedisHostAndPort sentinel : redisClusters) {
redisURI.getRedisClusters().add(
redisConnectionConfig.getRedisClusters().add(
new RedisConnectionConfig(sentinel.getHost(), sentinel.getPort(), timeout));
}
redisURI.setTimeout(timeout);
redisConnectionConfig.setTimeout(timeout);
return redisURI;
return redisConnectionConfig;
}
}
@ -665,4 +756,125 @@ public class RedisConnectionConfig {
private static boolean isValidPort(int port) {
return port >= 0 && port <= 65535;
}
/**
* Gets the value of trustedCertificatesPath.
*
* @return the value of trustedCertificatesPath
*/
public String getTrustedCertificatesPath() {
return trustedCertificatesPath;
}
/**
* Sets the trustedCertificatesPath.
* <p>
* <p>You can use getTrustedCertificatesPath() to get the value of trustedCertificatesPath</p>
*
* @param trustedCertificatesPath trustedCertificatesPath
*/
public void setTrustedCertificatesPath(String trustedCertificatesPath) {
this.trustedCertificatesPath = trustedCertificatesPath;
}
/**
* Gets the value of trustedCertificatesJksPassword.
*
* @return the value of trustedCertificatesJksPassword
*/
public String getTrustedCertificatesJksPassword() {
return trustedCertificatesJksPassword;
}
/**
* Sets the trustedCertificatesJksPassword.
* <p>
* <p>You can use getTrustedCertificatesJksPassword() to get the value of trustedCertificatesJksPassword</p>
*
* @param trustedCertificatesJksPassword trustedCertificatesJksPassword
*/
public void setTrustedCertificatesJksPassword(String trustedCertificatesJksPassword) {
this.trustedCertificatesJksPassword = trustedCertificatesJksPassword;
}
/**
* Gets the value of keyCertChainFilePath.
*
* @return the value of keyCertChainFilePath
*/
public String getKeyCertChainFilePath() {
return keyCertChainFilePath;
}
/**
* Sets the keyCertChainFilePath.
* <p>
* <p>You can use getKeyCertChainFilePath() to get the value of keyCertChainFilePath</p>
*
* @param keyCertChainFilePath keyCertChainFilePath
*/
public void setKeyCertChainFilePath(String keyCertChainFilePath) {
this.keyCertChainFilePath = keyCertChainFilePath;
}
/**
* Gets the value of keyFilePath.
*
* @return the value of keyFilePath
*/
public String getKeyFilePath() {
return keyFilePath;
}
/**
* Sets the keyFilePath.
* <p>
* <p>You can use getKeyFilePath() to get the value of keyFilePath</p>
*
* @param keyFilePath keyFilePath
*/
public void setKeyFilePath(String keyFilePath) {
this.keyFilePath = keyFilePath;
}
/**
* Gets the value of keyFilePassword.
*
* @return the value of keyFilePassword
*/
public String getKeyFilePassword() {
return keyFilePassword;
}
/**
* Sets the keyFilePassword.
* <p>
* <p>You can use getKeyFilePassword() to get the value of keyFilePassword</p>
*
* @param keyFilePassword keyFilePassword
*/
public void setKeyFilePassword(String keyFilePassword) {
this.keyFilePassword = keyFilePassword;
}
/**
* Sets the sslEnable.
* <p>
* <p>You can use isSslEnable() to get the value of sslEnable</p>
*
* @param sslEnable sslEnable
*/
public void setSslEnable(boolean sslEnable) {
this.sslEnable = sslEnable;
}
/**
* Gets the value of sslEnable.
*
* @return the value of sslEnable
*/
public boolean isSslEnable() {
return sslEnable;
}
}

View File

@ -35,6 +35,7 @@ public class RedisConnectionConfigTest {
Assert.assertEquals(host, redisConnectionConfig.getHost());
Assert.assertEquals(RedisConnectionConfig.DEFAULT_REDIS_PORT, redisConnectionConfig.getPort());
Assert.assertEquals(RedisConnectionConfig.DEFAULT_TIMEOUT_MILLISECONDS, redisConnectionConfig.getTimeout());
Assert.assertFalse(redisConnectionConfig.isSslEnable());
}
@Test
@ -133,4 +134,32 @@ public class RedisConnectionConfigTest {
Assert.assertNull(redisConnectionConfig.getHost());
Assert.assertEquals(3, redisConnectionConfig.getRedisClusters().size());
}
@Test
public void testRedisSsl() throws Exception {
String host = "localhost";
int port = 1879;
String trustedCertificatesPath = "trustedCertificatesPath";
String trustedCertificatesJksPassword = "trustedCertificatesJksPassword";
String keyCertChainFilePath = "keyCertChainFilePath";
String keyFilePath = "keyFilePath";
String keyFilePassword = "keyFilePassword";
RedisConnectionConfig redisConnectionConfig = RedisConnectionConfig.Builder.redis(host)
.withHost(host)
.withPort(port)
.withSslEnable(true)
.withTrustedCertificatesPath(trustedCertificatesPath)
.withTrustedCertificatesJksPassword(trustedCertificatesJksPassword)
.withKeyCertChainFilePath(keyCertChainFilePath)
.withKeyFilePath(keyFilePath)
.withKeyFilePassword(keyFilePassword)
.build();
Assert.assertTrue(redisConnectionConfig.isSslEnable());
Assert.assertEquals(redisConnectionConfig.getTrustedCertificatesPath(), trustedCertificatesPath);
Assert.assertEquals(redisConnectionConfig.getTrustedCertificatesJksPassword(), trustedCertificatesJksPassword);
Assert.assertEquals(redisConnectionConfig.getKeyCertChainFilePath(), keyCertChainFilePath);
Assert.assertEquals(redisConnectionConfig.getKeyFilePath(), keyFilePath);
Assert.assertEquals(redisConnectionConfig.getKeyFilePassword(), keyFilePassword);
}
}