Add support for Redis 6 authentication with username
Closes gh-22702
This commit is contained in:
@@ -60,11 +60,13 @@ abstract class RedisConnectionConfiguration {
|
||||
ConnectionInfo connectionInfo = parseUrl(this.properties.getUrl());
|
||||
config.setHostName(connectionInfo.getHostName());
|
||||
config.setPort(connectionInfo.getPort());
|
||||
config.setUsername(connectionInfo.getUsername());
|
||||
config.setPassword(RedisPassword.of(connectionInfo.getPassword()));
|
||||
}
|
||||
else {
|
||||
config.setHostName(this.properties.getHost());
|
||||
config.setPort(this.properties.getPort());
|
||||
config.setUsername(this.properties.getUsername());
|
||||
config.setPassword(RedisPassword.of(this.properties.getPassword()));
|
||||
}
|
||||
config.setDatabase(this.properties.getDatabase());
|
||||
@@ -80,6 +82,7 @@ abstract class RedisConnectionConfiguration {
|
||||
RedisSentinelConfiguration config = new RedisSentinelConfiguration();
|
||||
config.master(sentinelProperties.getMaster());
|
||||
config.setSentinels(createSentinels(sentinelProperties));
|
||||
config.setUsername(this.properties.getUsername());
|
||||
if (this.properties.getPassword() != null) {
|
||||
config.setPassword(RedisPassword.of(this.properties.getPassword()));
|
||||
}
|
||||
@@ -108,6 +111,7 @@ abstract class RedisConnectionConfiguration {
|
||||
if (clusterProperties.getMaxRedirects() != null) {
|
||||
config.setMaxRedirects(clusterProperties.getMaxRedirects());
|
||||
}
|
||||
config.setUsername(this.properties.getUsername());
|
||||
if (this.properties.getPassword() != null) {
|
||||
config.setPassword(RedisPassword.of(this.properties.getPassword()));
|
||||
}
|
||||
@@ -141,15 +145,20 @@ abstract class RedisConnectionConfiguration {
|
||||
throw new RedisUrlSyntaxException(url);
|
||||
}
|
||||
boolean useSsl = ("rediss".equals(scheme));
|
||||
String username = null;
|
||||
String password = null;
|
||||
if (uri.getUserInfo() != null) {
|
||||
password = uri.getUserInfo();
|
||||
int index = password.indexOf(':');
|
||||
String candidate = uri.getUserInfo();
|
||||
int index = candidate.indexOf(':');
|
||||
if (index >= 0) {
|
||||
password = password.substring(index + 1);
|
||||
username = candidate.substring(0, index);
|
||||
password = candidate.substring(index + 1);
|
||||
}
|
||||
else {
|
||||
password = candidate;
|
||||
}
|
||||
}
|
||||
return new ConnectionInfo(uri, useSsl, password);
|
||||
return new ConnectionInfo(uri, useSsl, username, password);
|
||||
}
|
||||
catch (URISyntaxException ex) {
|
||||
throw new RedisUrlSyntaxException(url, ex);
|
||||
@@ -162,11 +171,14 @@ abstract class RedisConnectionConfiguration {
|
||||
|
||||
private final boolean useSsl;
|
||||
|
||||
private final String username;
|
||||
|
||||
private final String password;
|
||||
|
||||
ConnectionInfo(URI uri, boolean useSsl, String password) {
|
||||
ConnectionInfo(URI uri, boolean useSsl, String username, String password) {
|
||||
this.uri = uri;
|
||||
this.useSsl = useSsl;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@@ -182,6 +194,10 @@ abstract class RedisConnectionConfiguration {
|
||||
return this.uri.getPort();
|
||||
}
|
||||
|
||||
String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,11 @@ public class RedisProperties {
|
||||
*/
|
||||
private String host = "localhost";
|
||||
|
||||
/**
|
||||
* Login username of the redis server.
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* Login password of the redis server.
|
||||
*/
|
||||
@@ -118,6 +123,14 @@ public class RedisProperties {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration.JedisClientConfigurationBuilder;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@@ -61,6 +62,7 @@ class RedisAutoConfigurationJedisTests {
|
||||
JedisConnectionFactory cf = context.getBean(JedisConnectionFactory.class);
|
||||
assertThat(cf.getHostName()).isEqualTo("foo");
|
||||
assertThat(cf.getDatabase()).isEqualTo(1);
|
||||
assertThat(getUserName(cf)).isNull();
|
||||
assertThat(cf.getPassword()).isNull();
|
||||
assertThat(cf.isUseSsl()).isFalse();
|
||||
});
|
||||
@@ -82,6 +84,7 @@ class RedisAutoConfigurationJedisTests {
|
||||
JedisConnectionFactory cf = context.getBean(JedisConnectionFactory.class);
|
||||
assertThat(cf.getHostName()).isEqualTo("example");
|
||||
assertThat(cf.getPort()).isEqualTo(33);
|
||||
assertThat(getUserName(cf)).isEqualTo("user");
|
||||
assertThat(cf.getPassword()).isEqualTo("password");
|
||||
assertThat(cf.isUseSsl()).isFalse();
|
||||
});
|
||||
@@ -96,6 +99,7 @@ class RedisAutoConfigurationJedisTests {
|
||||
JedisConnectionFactory cf = context.getBean(JedisConnectionFactory.class);
|
||||
assertThat(cf.getHostName()).isEqualTo("example");
|
||||
assertThat(cf.getPort()).isEqualTo(33);
|
||||
assertThat(getUserName(cf)).isEqualTo("user");
|
||||
assertThat(cf.getPassword()).isEqualTo("password");
|
||||
assertThat(cf.isUseSsl()).isTrue();
|
||||
});
|
||||
@@ -104,18 +108,22 @@ class RedisAutoConfigurationJedisTests {
|
||||
@Test
|
||||
void testPasswordInUrlWithColon() {
|
||||
this.contextRunner.withPropertyValues("spring.redis.url:redis://:pass:word@example:33").run((context) -> {
|
||||
assertThat(context.getBean(JedisConnectionFactory.class).getHostName()).isEqualTo("example");
|
||||
assertThat(context.getBean(JedisConnectionFactory.class).getPort()).isEqualTo(33);
|
||||
assertThat(context.getBean(JedisConnectionFactory.class).getPassword()).isEqualTo("pass:word");
|
||||
JedisConnectionFactory cf = context.getBean(JedisConnectionFactory.class);
|
||||
assertThat(cf.getHostName()).isEqualTo("example");
|
||||
assertThat(cf.getPort()).isEqualTo(33);
|
||||
assertThat(getUserName(cf)).isEqualTo("");
|
||||
assertThat(cf.getPassword()).isEqualTo("pass:word");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPasswordInUrlStartsWithColon() {
|
||||
this.contextRunner.withPropertyValues("spring.redis.url:redis://user::pass:word@example:33").run((context) -> {
|
||||
assertThat(context.getBean(JedisConnectionFactory.class).getHostName()).isEqualTo("example");
|
||||
assertThat(context.getBean(JedisConnectionFactory.class).getPort()).isEqualTo(33);
|
||||
assertThat(context.getBean(JedisConnectionFactory.class).getPassword()).isEqualTo(":pass:word");
|
||||
JedisConnectionFactory cf = context.getBean(JedisConnectionFactory.class);
|
||||
assertThat(cf.getHostName()).isEqualTo("example");
|
||||
assertThat(cf.getPort()).isEqualTo(33);
|
||||
assertThat(getUserName(cf)).isEqualTo("user");
|
||||
assertThat(cf.getPassword()).isEqualTo(":pass:word");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -178,13 +186,13 @@ class RedisAutoConfigurationJedisTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRedisConfigurationWithSentinelAndPassword() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.redis.password=password", "spring.redis.sentinel.master:mymaster",
|
||||
"spring.redis.sentinel.nodes:127.0.0.1:26379,127.0.0.1:26380")
|
||||
void testRedisConfigurationWithSentinelAndAuthentication() {
|
||||
this.contextRunner.withPropertyValues("spring.redis.username=user", "spring.redis.password=password",
|
||||
"spring.redis.sentinel.master:mymaster", "spring.redis.sentinel.nodes:127.0.0.1:26379,127.0.0.1:26380")
|
||||
.withUserConfiguration(JedisConnectionFactoryCaptorConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasFailed();
|
||||
assertThat(JedisConnectionFactoryCaptor.connectionFactory.isRedisSentinelAware()).isTrue();
|
||||
assertThat(getUserName(JedisConnectionFactoryCaptor.connectionFactory)).isEqualTo("user");
|
||||
assertThat(JedisConnectionFactoryCaptor.connectionFactory.getPassword()).isEqualTo("password");
|
||||
});
|
||||
}
|
||||
@@ -196,6 +204,10 @@ class RedisAutoConfigurationJedisTests {
|
||||
.isNotNull());
|
||||
}
|
||||
|
||||
private String getUserName(JedisConnectionFactory factory) {
|
||||
return ReflectionTestUtils.invokeMethod(factory, "getRedisUsername");
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomConfiguration {
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ class RedisAutoConfigurationTests {
|
||||
LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(cf.getHostName()).isEqualTo("foo");
|
||||
assertThat(cf.getDatabase()).isEqualTo(1);
|
||||
assertThat(getUserName(cf)).isNull();
|
||||
assertThat(cf.getPassword()).isNull();
|
||||
assertThat(cf.isUseSsl()).isFalse();
|
||||
assertThat(cf.getShutdownTimeout()).isEqualTo(500);
|
||||
@@ -110,6 +111,7 @@ class RedisAutoConfigurationTests {
|
||||
LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(cf.getHostName()).isEqualTo("example");
|
||||
assertThat(cf.getPort()).isEqualTo(33);
|
||||
assertThat(getUserName(cf)).isEqualTo("user");
|
||||
assertThat(cf.getPassword()).isEqualTo("password");
|
||||
assertThat(cf.isUseSsl()).isFalse();
|
||||
});
|
||||
@@ -124,6 +126,7 @@ class RedisAutoConfigurationTests {
|
||||
LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(cf.getHostName()).isEqualTo("example");
|
||||
assertThat(cf.getPort()).isEqualTo(33);
|
||||
assertThat(getUserName(cf)).isEqualTo("user");
|
||||
assertThat(cf.getPassword()).isEqualTo("password");
|
||||
assertThat(cf.isUseSsl()).isTrue();
|
||||
});
|
||||
@@ -135,6 +138,7 @@ class RedisAutoConfigurationTests {
|
||||
LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(cf.getHostName()).isEqualTo("example");
|
||||
assertThat(cf.getPort()).isEqualTo(33);
|
||||
assertThat(getUserName(cf)).isEqualTo("");
|
||||
assertThat(cf.getPassword()).isEqualTo("pass:word");
|
||||
});
|
||||
}
|
||||
@@ -145,6 +149,7 @@ class RedisAutoConfigurationTests {
|
||||
LettuceConnectionFactory cf = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(cf.getHostName()).isEqualTo("example");
|
||||
assertThat(cf.getPort()).isEqualTo(33);
|
||||
assertThat(getUserName(cf)).isEqualTo("user");
|
||||
assertThat(cf.getPassword()).isEqualTo(":pass:word");
|
||||
});
|
||||
}
|
||||
@@ -237,10 +242,12 @@ class RedisAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRedisConfigurationWithSentinelAndDataNodePassword() {
|
||||
this.contextRunner.withPropertyValues("spring.redis.password=password", "spring.redis.sentinel.master:mymaster",
|
||||
void testRedisConfigurationWithSentinelAndAuthentication() {
|
||||
this.contextRunner.withPropertyValues("spring.redis.username=user", "spring.redis.password=password",
|
||||
"spring.redis.sentinel.master:mymaster",
|
||||
"spring.redis.sentinel.nodes:127.0.0.1:26379, 127.0.0.1:26380").run((context) -> {
|
||||
LettuceConnectionFactory connectionFactory = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(getUserName(connectionFactory)).isEqualTo("user");
|
||||
assertThat(connectionFactory.getPassword()).isEqualTo("password");
|
||||
RedisSentinelConfiguration sentinelConfiguration = connectionFactory.getSentinelConfiguration();
|
||||
assertThat(sentinelConfiguration.getSentinelPassword().isPresent()).isFalse();
|
||||
@@ -256,6 +263,7 @@ class RedisAutoConfigurationTests {
|
||||
"spring.redis.sentinel.master:mymaster",
|
||||
"spring.redis.sentinel.nodes:127.0.0.1:26379, 127.0.0.1:26380").run((context) -> {
|
||||
LettuceConnectionFactory connectionFactory = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(getUserName(connectionFactory)).isNull();
|
||||
assertThat(connectionFactory.getPassword()).isEqualTo("password");
|
||||
RedisSentinelConfiguration sentinelConfiguration = connectionFactory.getSentinelConfiguration();
|
||||
assertThat(new String(sentinelConfiguration.getSentinelPassword().get())).isEqualTo("secret");
|
||||
@@ -292,16 +300,17 @@ class RedisAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRedisConfigurationWithClusterAndPassword() {
|
||||
void testRedisConfigurationWithClusterAndAuthentication() {
|
||||
List<String> clusterNodes = Arrays.asList("127.0.0.1:27379", "127.0.0.1:27380");
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.redis.password=password",
|
||||
"spring.redis.cluster.nodes[0]:" + clusterNodes.get(0),
|
||||
"spring.redis.cluster.nodes[1]:" + clusterNodes.get(1))
|
||||
.run((context) -> assertThat(context.getBean(LettuceConnectionFactory.class).getPassword())
|
||||
.isEqualTo("password")
|
||||
this.contextRunner.withPropertyValues("spring.redis.username=user", "spring.redis.password=password",
|
||||
"spring.redis.cluster.nodes[0]:" + clusterNodes.get(0),
|
||||
"spring.redis.cluster.nodes[1]:" + clusterNodes.get(1)).run((context) -> {
|
||||
LettuceConnectionFactory connectionFactory = context.getBean(LettuceConnectionFactory.class);
|
||||
assertThat(getUserName(connectionFactory)).isEqualTo("user");
|
||||
assertThat(connectionFactory.getPassword()).isEqualTo("password");
|
||||
}
|
||||
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -393,6 +402,10 @@ class RedisAutoConfigurationTests {
|
||||
return (LettucePoolingClientConfiguration) ReflectionTestUtils.getField(factory, "clientConfiguration");
|
||||
}
|
||||
|
||||
private String getUserName(LettuceConnectionFactory factory) {
|
||||
return ReflectionTestUtils.invokeMethod(factory, "getRedisUsername");
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class CustomConfiguration {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user