Add support for Redis 6 authentication with username

Closes gh-22702
pull/23540/head
Stephane Nicoll 4 years ago
parent a5c20a5132
commit 36382e599c

@ -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 {

Loading…
Cancel
Save