diff --git a/Makefile b/Makefile
index 07f7c0ce92..e23a1519fc 100644
--- a/Makefile
+++ b/Makefile
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-REDIS_VERSION:=5.0.5
+REDIS_VERSION:=6.0.7
SPRING_PROFILE?=ci
SHELL=/bin/bash -euo pipefail
@@ -36,6 +36,24 @@ work/redis-%.conf:
echo save \"\" >> $@
echo slaveof 127.0.0.1 6379 >> $@
+# Handled separately because it's a node with authentication. User: spring, password: data. Default password: foobared
+work/redis-6382.conf:
+ @mkdir -p $(@D)
+
+ echo port 6382 >> $@
+ echo daemonize yes >> $@
+ echo protected-mode no >> $@
+ echo bind 0.0.0.0 >> $@
+ echo notify-keyspace-events Ex >> $@
+ echo pidfile $(shell pwd)/work/redis-6382.pid >> $@
+ echo logfile $(shell pwd)/work/redis-6382.log >> $@
+ echo unixsocket $(shell pwd)/work/redis-6382.sock >> $@
+ echo unixsocketperm 755 >> $@
+ echo "requirepass foobared" >> $@
+ echo "user default on #1b58ee375b42e41f0e48ef2ff27d10a5b1f6924a9acdcdba7cae868e7adce6bf ~* +@all" >> $@
+ echo "user spring on #3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7 +@all" >> $@
+ echo save \"\" >> $@
+
# Handled separately because it's the master and all others are slaves
work/redis-6379.conf:
@mkdir -p $(@D)
@@ -54,9 +72,9 @@ work/redis-6379.conf:
work/redis-%.pid: work/redis-%.conf work/redis/bin/redis-server
work/redis/bin/redis-server $<
-redis-start: work/redis-6379.pid work/redis-6380.pid work/redis-6381.pid
+redis-start: work/redis-6379.pid work/redis-6380.pid work/redis-6381.pid work/redis-6382.pid
-redis-stop: stop-6379 stop-6380 stop-6381
+redis-stop: stop-6379 stop-6380 stop-6381 stop-6382
##########
# Sentinel
@@ -75,12 +93,29 @@ work/sentinel-%.conf:
echo save \"\" >> $@
echo sentinel monitor mymaster 127.0.0.1 6379 2 >> $@
+# Password-protected Sentinel
+work/sentinel-26382.conf:
+ @mkdir -p $(@D)
+
+ echo port 26382 >> $@
+ echo daemonize yes >> $@
+ echo protected-mode no >> $@
+ echo bind 0.0.0.0 >> $@
+ echo pidfile $(shell pwd)/work/sentinel-26382.pid >> $@
+ echo logfile $(shell pwd)/work/sentinel-26382.log >> $@
+ echo save \"\" >> $@
+ echo "requirepass foobared" >> $@
+ echo "user default on #1b58ee375b42e41f0e48ef2ff27d10a5b1f6924a9acdcdba7cae868e7adce6bf ~* +@all" >> $@
+ echo "user spring on #3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7 +@all" >> $@
+ echo sentinel monitor mymaster 127.0.0.1 6382 2 >> $@
+ echo sentinel auth-pass mymaster foobared >> $@
+
work/sentinel-%.pid: work/sentinel-%.conf work/redis-6379.pid work/redis/bin/redis-server
work/redis/bin/redis-server $< --sentinel
-sentinel-start: work/sentinel-26379.pid work/sentinel-26380.pid work/sentinel-26381.pid
+sentinel-start: work/sentinel-26379.pid work/sentinel-26380.pid work/sentinel-26381.pid work/sentinel-26382.pid
-sentinel-stop: stop-26379 stop-26380 stop-26381
+sentinel-stop: stop-26379 stop-26380 stop-26381 stop-26382
#########
@@ -150,6 +185,12 @@ start: redis-start sentinel-start cluster-init
stop-%: work/redis/bin/redis-cli
-work/redis/bin/redis-cli -p $* shutdown
+stop-6382: work/redis/bin/redis-cli
+ -work/redis/bin/redis-cli -a foobared -p 6382 shutdown
+
+stop-26382: work/redis/bin/redis-cli
+ -work/redis/bin/redis-cli -a foobared -p 26382 shutdown
+
stop: redis-stop sentinel-stop cluster-stop
test:
diff --git a/pom.xml b/pom.xml
index 1403fde28e..6f2ee98641 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-redis
- 2.4.0-SNAPSHOT
+ 2.4.0-DATAREDIS-1046-SNAPSHOT
Spring Data Redis
@@ -22,7 +22,7 @@
1.9.2
1.4.12
2.7.0
- 5.3.3.RELEASE
+ 6.0.0.RC1
3.3.0
1.01
4.1.51.Final
diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc
index 5ae24df584..414264c332 100644
--- a/src/main/asciidoc/new-features.adoc
+++ b/src/main/asciidoc/new-features.adoc
@@ -7,9 +7,10 @@ This section briefly covers items that are new and noteworthy in the latest rele
== New in Spring Data Redis 2.4
* `RedisCache` now exposes `CacheStatistics`.
+* ACL authentication support for Redis Standalone, Redis Cluster and Master/Replica.
+* Password support for Redis Sentinel using Jedis.
[[new-in-2.3.0]]
-
== New in Spring Data Redis 2.3
* Template API Method Refinements for `Duration` and `Instant`.
diff --git a/src/main/asciidoc/reference/redis.adoc b/src/main/asciidoc/reference/redis.adoc
index 2ea34a7925..5a719c387b 100644
--- a/src/main/asciidoc/reference/redis.adoc
+++ b/src/main/asciidoc/reference/redis.adoc
@@ -324,11 +324,6 @@ public RedisConnectionFactory lettuceConnectionFactory() {
Sometimes, direct interaction with one of the Sentinels is required. Using `RedisConnectionFactory.getSentinelConnection()` or `RedisConnection.getSentinelCommands()` gives you access to the first active Sentinel configured.
-[NOTE]
-====
-Sentinel authentication is only available using https://lettuce.io/[Lettuce].
-====
-
[[redis:template]]
== Working with Objects through RedisTemplate
diff --git a/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java
index 236ff1c2a6..7947da1f81 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisClusterConfiguration.java
@@ -23,6 +23,7 @@
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import org.springframework.core.env.MapPropertySource;
@@ -49,6 +50,7 @@ public class RedisClusterConfiguration implements RedisConfiguration, ClusterCon
private Set clusterNodes;
private @Nullable Integer maxRedirects;
+ private Optional username = Optional.empty();
private RedisPassword password = RedisPassword.none();
/**
@@ -182,6 +184,24 @@ private void appendClusterNodes(Set hostAndPorts) {
}
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#setUsername(String)
+ */
+ @Override
+ public void setUsername(String username) {
+ this.username = Optional.of(username);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#getUsername()
+ */
+ @Override
+ public Optional getUsername() {
+ return this.username;
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getPassword()
diff --git a/src/main/java/org/springframework/data/redis/connection/RedisConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisConfiguration.java
index 523d32af00..e860cf8819 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisConfiguration.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisConfiguration.java
@@ -17,6 +17,7 @@
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
@@ -51,11 +52,11 @@ default Integer getDatabaseOrElse(Supplier other) {
/**
* Get the configured {@link RedisPassword} if the current {@link RedisConfiguration} is
- * {@link #isPasswordAware(RedisConfiguration) password aware} or evaluate and return the value of the given
+ * {@link #isAuthenticationAware(RedisConfiguration) password aware} or evaluate and return the value of the given
* {@link Supplier}.
*
* @param other a {@code Supplier} whose result is returned if given {@link RedisConfiguration} is not
- * {@link #isPasswordAware(RedisConfiguration) password aware}.
+ * {@link #isAuthenticationAware(RedisConfiguration) password aware}.
* @return never {@literal null}.
* @throws IllegalArgumentException if {@code other} is {@literal null}.
*/
@@ -67,8 +68,8 @@ default RedisPassword getPasswordOrElse(Supplier other) {
* @param configuration can be {@literal null}.
* @return {@code true} if given {@link RedisConfiguration} is instance of {@link WithPassword}.
*/
- static boolean isPasswordAware(@Nullable RedisConfiguration configuration) {
- return configuration instanceof WithPassword;
+ static boolean isAuthenticationAware(@Nullable RedisConfiguration configuration) {
+ return configuration instanceof WithAuthentication;
}
/**
@@ -136,14 +137,28 @@ static Integer getDatabaseOrElse(@Nullable RedisConfiguration configuration, Sup
/**
* @param configuration can be {@literal null}.
* @param other a {@code Supplier} whose result is returned if given {@link RedisConfiguration} is not
- * {@link #isPasswordAware(RedisConfiguration) password aware}.
+ * {@link #isAuthenticationAware(RedisConfiguration) password aware}.
+ * @return never {@literal null}.
+ * @throws IllegalArgumentException if {@code other} is {@literal null}.
+ */
+ static Optional getUsernameOrElse(@Nullable RedisConfiguration configuration,
+ Supplier> other) {
+
+ Assert.notNull(other, "Other must not be null!");
+ return isAuthenticationAware(configuration) ? ((WithAuthentication) configuration).getUsername() : other.get();
+ }
+
+ /**
+ * @param configuration can be {@literal null}.
+ * @param other a {@code Supplier} whose result is returned if given {@link RedisConfiguration} is not
+ * {@link #isAuthenticationAware(RedisConfiguration) password aware}.
* @return never {@literal null}.
* @throws IllegalArgumentException if {@code other} is {@literal null}.
*/
static RedisPassword getPasswordOrElse(@Nullable RedisConfiguration configuration, Supplier other) {
Assert.notNull(other, "Other must not be null!");
- return isPasswordAware(configuration) ? ((WithPassword) configuration).getPassword() : other.get();
+ return isAuthenticationAware(configuration) ? ((WithAuthentication) configuration).getPassword() : other.get();
}
/**
@@ -178,9 +193,17 @@ static String getHostOrElse(@Nullable RedisConfiguration configuration, Supplier
* {@link RedisConfiguration} part suitable for configurations that may use authentication when connecting.
*
* @author Christoph Strobl
- * @since 2.1
+ * @author Mark Paluch
+ * @since 2.4
*/
- interface WithPassword {
+ interface WithAuthentication {
+
+ /**
+ * Create and set a username with the given {@link String}. Requires Redis 6 or newer.
+ *
+ * @param username the username.
+ */
+ void setUsername(String username);
/**
* Create and set a {@link RedisPassword} for given {@link String}.
@@ -207,6 +230,13 @@ default void setPassword(@Nullable char[] password) {
*/
void setPassword(RedisPassword password);
+ /**
+ * Get the username to use when connecting.
+ *
+ * @return {@link Optional#empty()} if none set.
+ */
+ Optional getUsername();
+
/**
* Get the RedisPassword to use when connecting.
*
@@ -215,6 +245,16 @@ default void setPassword(@Nullable char[] password) {
RedisPassword getPassword();
}
+ /**
+ * {@link RedisConfiguration} part suitable for configurations that may use authentication when connecting.
+ *
+ * @author Christoph Strobl
+ * @since 2.1
+ */
+ interface WithPassword extends WithAuthentication {
+
+ }
+
/**
* {@link RedisConfiguration} part suitable for configurations that use a specific database.
*
@@ -339,7 +379,17 @@ default void setMaster(final String name) {
Set getSentinels();
/**
- * Get the {@link RedisPassword} used when authenticating with a Redis Server..
+ * Get the username used when authenticating with a Redis Server.
+ *
+ * @return never {@literal null}.
+ * @since 2.4
+ */
+ default Optional getDataNodeUsername() {
+ return getUsername();
+ }
+
+ /**
+ * Get the {@link RedisPassword} used when authenticating with a Redis Server.
*
* @return never {@literal null}.
* @since 2.2.2
diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java
index 406e7bb728..8313b7a764 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisSentinelConfiguration.java
@@ -21,6 +21,7 @@
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import org.springframework.core.env.MapPropertySource;
@@ -50,6 +51,7 @@ public class RedisSentinelConfiguration implements RedisConfiguration, SentinelC
private Set sentinels;
private int database;
+ private Optional dataNodeUsername = Optional.empty();
private RedisPassword dataNodePassword = RedisPassword.none();
private RedisPassword sentinelPassword = RedisPassword.none();
@@ -229,6 +231,24 @@ public void setDatabase(int index) {
this.database = index;
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#setUsername(String)
+ */
+ @Override
+ public void setUsername(String username) {
+ this.dataNodeUsername = Optional.of(username);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#getUsername()
+ */
+ @Override
+ public Optional getUsername() {
+ return this.dataNodeUsername;
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getPassword()
diff --git a/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java
index c2b4336245..93118a5724 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisSocketConfiguration.java
@@ -15,6 +15,8 @@
*/
package org.springframework.data.redis.connection;
+import java.util.Optional;
+
import org.springframework.data.redis.connection.RedisConfiguration.DomainSocketConfiguration;
import org.springframework.util.Assert;
@@ -32,6 +34,7 @@ public class RedisSocketConfiguration implements RedisConfiguration, DomainSocke
private String socket = DEFAULT_SOCKET;
private int database;
+ private Optional username = Optional.empty();
private RedisPassword password = RedisPassword.none();
/**
@@ -92,6 +95,24 @@ public void setDatabase(int index) {
this.database = index;
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#setUsername(String)
+ */
+ @Override
+ public void setUsername(String username) {
+ this.username = Optional.of(username);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#getUsername()
+ */
+ @Override
+ public Optional getUsername() {
+ return this.username;
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getPassword()
diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java
index 12d2a96a0c..058b32af12 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisStandaloneConfiguration.java
@@ -15,6 +15,8 @@
*/
package org.springframework.data.redis.connection;
+import java.util.Optional;
+
import org.springframework.data.redis.connection.RedisConfiguration.WithDatabaseIndex;
import org.springframework.data.redis.connection.RedisConfiguration.WithHostAndPort;
import org.springframework.data.redis.connection.RedisConfiguration.WithPassword;
@@ -37,6 +39,7 @@ public class RedisStandaloneConfiguration
private String hostName = DEFAULT_HOST;
private int port = DEFAULT_PORT;
private int database;
+ private Optional username = Optional.empty();
private RedisPassword password = RedisPassword.none();
/**
@@ -125,6 +128,24 @@ public void setDatabase(int index) {
this.database = index;
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#setUsername(String)
+ */
+ @Override
+ public void setUsername(String username) {
+ this.username = Optional.of(username);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#getUsername()
+ */
+ @Override
+ public Optional getUsername() {
+ return this.username;
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getPassword()
diff --git a/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java b/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java
index c7a1ad2931..d31f5939e6 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisStaticMasterReplicaConfiguration.java
@@ -18,6 +18,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import org.springframework.data.redis.connection.RedisConfiguration.StaticMasterReplicaConfiguration;
import org.springframework.util.Assert;
@@ -40,6 +41,7 @@ public class RedisStaticMasterReplicaConfiguration implements RedisConfiguration
private List nodes = new ArrayList<>();
private int database;
+ private Optional username = Optional.empty();
private RedisPassword password = RedisPassword.none();
/**
@@ -130,6 +132,24 @@ public void setDatabase(int index) {
this.nodes.forEach(it -> it.setDatabase(database));
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#setUsername(String)
+ */
+ @Override
+ public void setUsername(String username) {
+ this.username = Optional.of(username);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.connection.RedisConfiguration.WithAuthentication#getUsername()
+ */
+ @Override
+ public Optional getUsername() {
+ return this.username;
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisConfiguration.WithPassword#getPassword()
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java
index d65f8ca096..8cb3fbb6c1 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnectionFactory.java
@@ -328,6 +328,7 @@ public void afterPropertiesSet() {
clientConfiguration.getHostnameVerifier().orElse(null));
getRedisPassword().map(String::new).ifPresent(shardInfo::setPassword);
+ getRedisUsername().ifPresent(shardInfo::setUser);
int readTimeout = getReadTimeout();
@@ -369,9 +370,13 @@ private Pool createPool() {
*/
protected Pool createRedisSentinelPool(RedisSentinelConfiguration config) {
- GenericObjectPoolConfig poolConfig = getPoolConfig() != null ? getPoolConfig() : new JedisPoolConfig();
+ GenericObjectPoolConfig> poolConfig = getPoolConfig() != null ? getPoolConfig() : new JedisPoolConfig();
+ String sentinelUser = null;
+ String sentinelPassword = config.getSentinelPassword().toOptional().map(String::new).orElse(null);
+
return new JedisSentinelPool(config.getMaster().getName(), convertToJedisSentinelSet(config.getSentinels()),
- poolConfig, getConnectTimeout(), getReadTimeout(), getPassword(), getDatabase(), getClientName());
+ poolConfig, getConnectTimeout(), getReadTimeout(), getUsername(), getPassword(), getDatabase(),
+ getClientName(), getConnectTimeout(), getReadTimeout(), sentinelUser, sentinelPassword, getClientName());
}
/**
@@ -383,7 +388,7 @@ protected Pool createRedisSentinelPool(RedisSentinelConfiguration config)
protected Pool createRedisPool() {
return new JedisPool(getPoolConfig(), getHostName(), getPort(), getConnectTimeout(), getReadTimeout(),
- getPassword(), getDatabase(), getClientName(), isUseSsl(),
+ getUsername(), getPassword(), getDatabase(), getClientName(), isUseSsl(),
clientConfiguration.getSslSocketFactory().orElse(null), //
clientConfiguration.getSslParameters().orElse(null), //
clientConfiguration.getHostnameVerifier().orElse(null));
@@ -414,7 +419,7 @@ protected ClusterTopologyProvider createTopologyProvider(JedisCluster cluster) {
* @return the actual {@link JedisCluster}.
* @since 1.7
*/
- protected JedisCluster createCluster(RedisClusterConfiguration clusterConfig, GenericObjectPoolConfig poolConfig) {
+ protected JedisCluster createCluster(RedisClusterConfiguration clusterConfig, GenericObjectPoolConfig> poolConfig) {
Assert.notNull(clusterConfig, "Cluster configuration must not be null!");
@@ -425,7 +430,7 @@ protected JedisCluster createCluster(RedisClusterConfiguration clusterConfig, Ge
int redirects = clusterConfig.getMaxRedirects() != null ? clusterConfig.getMaxRedirects() : 5;
- return new JedisCluster(hostAndPort, getConnectTimeout(), getReadTimeout(), redirects, getPassword(),
+ return new JedisCluster(hostAndPort, getConnectTimeout(), getReadTimeout(), redirects, getUsername(), getPassword(),
getClientName(), poolConfig, isUseSsl(), clientConfiguration.getSslSocketFactory().orElse(null),
clientConfiguration.getSslParameters().orElse(null), clientConfiguration.getHostnameVerifier().orElse(null),
null);
@@ -544,6 +549,16 @@ public void setUseSsl(boolean useSsl) {
getMutableConfiguration().setUseSsl(useSsl);
}
+ /**
+ * Returns the username used for authenticating with the Redis server.
+ *
+ * @return username for authentication.
+ */
+ @Nullable
+ private String getUsername() {
+ return getRedisUsername().orElse(null);
+ }
+
/**
* Returns the password used for authenticating with the Redis server.
*
@@ -554,6 +569,10 @@ public String getPassword() {
return getRedisPassword().map(String::new).orElse(null);
}
+ private Optional getRedisUsername() {
+ return RedisConfiguration.getUsernameOrElse(this.configuration, standaloneConfig::getUsername);
+ }
+
private RedisPassword getRedisPassword() {
return RedisConfiguration.getPasswordOrElse(this.configuration, standaloneConfig::getPassword);
}
@@ -568,7 +587,7 @@ private RedisPassword getRedisPassword() {
@Deprecated
public void setPassword(String password) {
- if (RedisConfiguration.isPasswordAware(configuration)) {
+ if (RedisConfiguration.isAuthenticationAware(configuration)) {
((WithPassword) configuration).setPassword(password);
return;
@@ -851,10 +870,12 @@ public RedisSentinelConnection getSentinelConnection() {
private Jedis getActiveSentinel() {
Assert.isTrue(RedisConfiguration.isSentinelConfiguration(configuration), "SentinelConfig must not be null!");
+ SentinelConfiguration sentinelConfiguration = (SentinelConfiguration) configuration;
- for (RedisNode node : ((SentinelConfiguration) configuration).getSentinels()) {
+ for (RedisNode node : sentinelConfiguration.getSentinels()) {
Jedis jedis = new Jedis(node.getHost(), node.getPort(), getConnectTimeout(), getReadTimeout());
+ sentinelConfiguration.getSentinelPassword().toOptional().map(String::new).ifPresent(jedis::auth);
try {
if (jedis.ping().equalsIgnoreCase("pong")) {
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java
index 3c67d8c787..ddc53b590f 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnectionFactory.java
@@ -788,6 +788,9 @@ public void setClientName(@Nullable String clientName) {
this.getMutableConfiguration().setClientName(clientName);
}
+ private Optional getRedisUsername() {
+ return RedisConfiguration.getUsernameOrElse(configuration, standaloneConfig::getUsername);
+ }
/**
* Returns the password used for authenticating with the Redis server.
*
@@ -812,7 +815,7 @@ private RedisPassword getRedisPassword() {
@Deprecated
public void setPassword(String password) {
- if (RedisConfiguration.isPasswordAware(configuration)) {
+ if (RedisConfiguration.isAuthenticationAware(configuration)) {
((WithPassword) configuration).setPassword(password);
return;
@@ -1135,7 +1138,8 @@ private RedisURI createRedisURIAndApplySettings(String host, int port) {
RedisURI.Builder builder = RedisURI.Builder.redis(host, port);
- getRedisPassword().toOptional().ifPresent(builder::withPassword);
+ applyAuthentication(builder);
+
clientConfiguration.getClientName().ifPresent(builder::withClientName);
builder.withDatabase(getDatabase());
@@ -1151,13 +1155,25 @@ private RedisURI createRedisSocketURIAndApplySettings(String socketPath) {
RedisURI.Builder builder = RedisURI.Builder.socket(socketPath);
- getRedisPassword().toOptional().ifPresent(builder::withPassword);
+ applyAuthentication(builder);
builder.withDatabase(getDatabase());
builder.withTimeout(clientConfiguration.getCommandTimeout());
return builder.build();
}
+ private void applyAuthentication(RedisURI.Builder builder) {
+
+ Optional username = getRedisUsername();
+ if (username.isPresent()) {
+ // See https://github.com/lettuce-io/lettuce-core/issues/1404
+ username.ifPresent(
+ it -> builder.withAuthentication(it, new String(getRedisPassword().toOptional().orElse(new char[0]))));
+ } else {
+ getRedisPassword().toOptional().ifPresent(builder::withPassword);
+ }
+ }
+
@Override
public RedisSentinelConnection getSentinelConnection() {
return new LettuceSentinelConnection(connectionProvider);
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java
index 44be5a7254..2d90e28b1c 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConverters.java
@@ -18,16 +18,11 @@
import io.lettuce.core.*;
import io.lettuce.core.cluster.models.partitions.Partitions;
import io.lettuce.core.cluster.models.partitions.RedisClusterNode.NodeFlag;
-import io.lettuce.core.models.stream.PendingMessage;
-import io.lettuce.core.models.stream.PendingMessages;
-import io.lettuce.core.models.stream.PendingParser;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
-import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
-import java.util.function.BiFunction;
import java.util.stream.Collectors;
import org.springframework.core.convert.converter.Converter;
@@ -69,9 +64,6 @@
import org.springframework.data.redis.connection.convert.ListConverter;
import org.springframework.data.redis.connection.convert.LongToBooleanConverter;
import org.springframework.data.redis.connection.convert.StringToRedisClientInfoConverter;
-import org.springframework.data.redis.connection.stream.Consumer;
-import org.springframework.data.redis.connection.stream.PendingMessagesSummary;
-import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.core.types.RedisClientInfo;
@@ -79,7 +71,6 @@
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
-import org.springframework.util.NumberUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
@@ -657,15 +648,19 @@ public static RedisURI sentinelConfigurationToRedisURI(RedisSentinelConfiguratio
RedisURI.Builder sentinelBuilder = RedisURI.Builder.redis(sentinel.getHost(), sentinel.getPort());
- if (sentinelPassword.isPresent()) {
- sentinelBuilder.withPassword(sentinelPassword.get());
- }
+ sentinelPassword.toOptional().ifPresent(sentinelBuilder::withPassword);
+
builder.withSentinel(sentinelBuilder.build());
}
+ Optional username = sentinelConfiguration.getUsername();
RedisPassword password = sentinelConfiguration.getPassword();
- if (password.isPresent()) {
- builder.withPassword(password.get());
+
+ if (username.isPresent()) {
+ // See https://github.com/lettuce-io/lettuce-core/issues/1404
+ builder.withAuthentication(username.get(), new String(password.toOptional().orElse(new char[0])));
+ } else {
+ password.toOptional().ifPresent(builder::withPassword);
}
builder.withSentinelMasterId(sentinelConfiguration.getMaster().getName());
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommands.java
index fca79aec86..ef3d0e6807 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveScriptingCommands.java
@@ -26,6 +26,7 @@
import org.springframework.data.redis.connection.ReactiveScriptingCommands;
import org.springframework.data.redis.connection.ReturnType;
+import org.springframework.data.redis.util.ByteUtils;
import org.springframework.util.Assert;
/**
@@ -80,7 +81,7 @@ public Mono scriptLoad(ByteBuffer script) {
Assert.notNull(script, "Script must not be null!");
- return connection.execute(cmd -> cmd.scriptLoad(script)).next();
+ return connection.execute(cmd -> cmd.scriptLoad(ByteUtils.getBytes(script))).next();
}
/*
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStreamCommands.java
index 2cb940c0a3..cf1baeb95a 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStreamCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveStreamCommands.java
@@ -21,15 +21,16 @@
import io.lettuce.core.XReadArgs;
import io.lettuce.core.XReadArgs.StreamOffset;
import io.lettuce.core.cluster.api.reactive.RedisClusterReactiveCommands;
+import io.lettuce.core.models.stream.PendingMessage;
import reactor.core.publisher.Flux;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import org.reactivestreams.Publisher;
+
import org.springframework.data.redis.connection.ReactiveRedisConnection.CommandResponse;
import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand;
import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse;
@@ -204,7 +205,7 @@ public Flux> xGroup(Publisher new CommandResponse<>(command, Boolean.TRUE.equals(it) ? "OK" : "Error"));
+ .map(it -> new CommandResponse<>(command, "OK"));
}
if (command.getAction().equals(GroupCommandAction.DESTROY)) {
@@ -243,25 +244,8 @@ public Flux> xPen
return connection.execute(cmd -> Flux.from(commands).concatMap(command -> {
Assert.notNull(command.getKey(), "Key must not be null!");
- return cmd.xpending(command.getKey(), ByteUtils.getByteBuffer(command.getGroupName())).collectList().map(it -> {
-
- // begin
- // {* hacking *}
- // while (https://github.com/lettuce-io/lettuce-core/issues/1229 != resolved) begin
-
- ArrayList