diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java index 1b3301f6e3..15bb5b2415 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisConnectionConfiguration.java @@ -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; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java index a9607fda4c..0dfb6474d8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/redis/RedisProperties.java @@ -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; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java index 4d44b2a638..a961df65cc 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationJedisTests.java @@ -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 { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java index b0095a94b1..91c2bbc92b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/redis/RedisAutoConfigurationTests.java @@ -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 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 {