java Letterfly无法使用SSL连接到Redis集群,但可以使用SSL连接到相同的Redis服务器,将其视为独立节点

zbdgwd5y  于 2023-11-15  发布在  Java
关注(0)|答案(3)|浏览(119)

我有一个Azure Cache for Redis - Premium and Cluster enabled。我一直试图使用spring-boot-starter-data-redis(spring Boot version:2.3.4.RELEASE,Java version:11)和使用lettuce客户端连接到该Redis,但当我将我的Redis视为Redis Cluster时,Letterfly抛出以下SSL异常,但当将其用作独立Redis服务器时,连接正常。
我的pom.xml依赖项是:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

字符串
Java代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.*;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;

@Configuration
class LettuceConfig {

    @Bean
    StringRedisTemplate getStringRedisTemplate(final RedisProperties redisProperties) {
        return new StringRedisTemplate(getRedisConnectionFactory(redisProperties));
    }

    @Bean
    RedisConnectionFactory getRedisConnectionFactory(final RedisProperties redisProperties) {
    
        final RedisNode redisNode = RedisNode.newRedisNode()
                .listeningAt(redisProperties.getHost(), redisProperties.getPort())
                .build();

        // Connecting as a Redis Cluster
        final RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
        redisClusterConfiguration.addClusterNode(redisNode);
        redisClusterConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));

        // Connecting as a Standalone Redis server
        final RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(redisProperties.getHost());
        redisStandaloneConfiguration.setPort(redisProperties.getPort());
        redisStandaloneConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));

        final LettuceClientConfiguration.LettuceClientConfigurationBuilder lettuceClientConfigurationBuilder =
                LettuceClientConfiguration.builder()
                .clientName(redisProperties.getClientName())
                .commandTimeout(redisProperties.getTimeout());

        if (redisProperties.isSsl()) {
            lettuceClientConfigurationBuilder.useSsl();
        }

        final LettuceClientConfiguration lettuceClientConfiguration = lettuceClientConfigurationBuilder.build();

        return new LettuceConnectionFactory(redisClusterConfiguration, lettuceClientConfiguration);
    }
}

@SpringBootApplication
public class LettuceClusterApplication implements CommandLineRunner {

    private final StringRedisTemplate stringRedisTemplate;

    @Autowired
    public LettuceClusterApplication(final StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    public static void main(String[] args) {
        SpringApplication.run(LettuceClusterApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println(stringRedisTemplate.hasKey("abc"));
    }
}


当在new LettuceConnectionFactory(..., ...)中使用redisStandaloneConfiguration时,代码工作正常,但如果我使用redisClusterConfiguration,代码会失败,但有以下例外:

java.lang.IllegalStateException: Failed to execute CommandLineRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) ~[spring-boot-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    ...
Caused by: org.springframework.data.redis.RedisConnectionFailureException: Redis connection failed; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to [RedisURI [host='<redacted>.redis.cache.windows.net', port=6380]]
    at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:66) ~[spring-data-redis-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    ...
Caused by: io.lettuce.core.RedisConnectionException: Unable to connect to [RedisURI [host='<redacted>.redis.cache.windows.net', port=6380]]
    at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:78) ~[lettuce-core-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    ...
Caused by: javax.net.ssl.SSLHandshakeException: No subject alternative names matching IP address <redacted> found
    ...
Caused by: java.security.cert.CertificateException: No subject alternative names matching IP address <redacted> found
    at java.base/sun.security.util.HostnameChecker.matchIP(HostnameChecker.java:165) ~[na:na]
    ...


我的application.properties文件:

spring.redis.host = <redacted>.redis.cache.windows.net
spring.redis.port = 6380
spring.redis.password = <redacted>
spring.redis.ssl = true
spring.redis.clientName = ${HOSTNAME}
spring.redis.timeout = 100000

更新:在Github中发现了类似的问题:https://github.com/lettuce-io/lettuce-core/issues/246,但它说它应该与lettuce版本> 4.2一起工作,而我的核心版本(捆绑在spring-boot-starter-data-redis下)是5.3.4.RELEASE。同样值得查看的是相同的文档:https://lettuce.io/core/release/reference/#ssl

Letterfly从版本3.1开始支持Redis Standalone连接,从版本4.2开始支持Redis Cluster连接
GitHub问题:https://github.com/lettuce-io/lettuce-core/issues/1454

aiqt4smr

aiqt4smr1#

如果所有节点的IP地址都与主机名相同(我认为Azure Cache for Redis就是这种情况),那么这是一种可以配置客户端将未解析的IP地址Map回证书中列出的主机名的方法。

@Bean
    ClientResources clientResources(RedisProperties redisProperties) throws UnknownHostException {
        var clientResourcesBuilder = DefaultClientResources.builder();
        var configuredHost = redisProperties.getHost();
        var inetAddresses = Arrays.asList(InetAddress.getAllByName(configuredHost));
        MappingSocketAddressResolver resolver = MappingSocketAddressResolver.create(
                DnsResolvers.UNRESOLVED,
                hostAndPort -> inetAddresses.stream()
                        .anyMatch(i -> i.getHostAddress().equals(hostAndPort.getHostText())) ?
                        HostAndPort.of(configuredHost, hostAndPort.getPort()) :
                        hostAndPort
        );
        clientResourcesBuilder.socketAddressResolver(resolver);
        return clientResourcesBuilder.build();
    }

字符串
据我所知,这是Github问题上列出的最好的解决方案,直到微软从他们的Angular 解决问题。

lf5gs5x2

lf5gs5x22#

与在独立模式下连接不同,在群集模式下连接到Azure Redis是一个两步过程:
1.连接到<hostname:6380>,进行身份验证,并获取群集端点详细信息
1.连接到您在集群端点详细信息中获得的<ip address:port>,再次进行身份验证,然后将命令发送到您的密钥所在的特定集群分片
您获取No subject alternative names matching IP address <redacted> found的原因是Azure redis在群集端点详细信息中为您提供了一个IP地址+端口号,然后Letterfly尝试根据IP地址(而不是主机名)验证您的SSL连接,但失败了,因为它试图根据您当前连接到<ip address>:<port>的服务器端点验证SSL证书主题或SAN something.redis.cache.windows.net
在大多数客户端库中,您可以通过配置或覆盖SSL证书验证来根据特定redis缓存的主机名验证服务器证书来解决这个问题。
例如,在.Net StackExchange.redis中有一个名为'sslhost'的配置设置可用于此目的。
希望莱特希泽有一个对等的。

j9per5c4

j9per5c43#

我们在使用spring.data.redis.ssl.enabled=true时得到以下错误。

Cannot connect Redis Sentinel at rediss://username:***********************************@xxx.xxx.xxx.xxx:26379:
java.util.concurrent.CompletionException: javax.net.ssl.SSLHandshakeException: 
No subject alternative names matching IP address xxx.xxx.xxx.xxx found

字符串
经过一些尝试和错误,我们从lettuce(这是Sping Boot 中与redis对话的默认库)改为jedis,然后它工作了:)
我们使用pom.xml中的以下代码实现了这一点:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>


我们正在使用Sping Boot 3.1.5和Java 21。

相关问题