Desbravando a Conexão Segura TLS com o Redis 7: Modificações Essenciais no Código para Java 8
Tags: redis, java, tls, security
Published: 2023-07-13 23:57:49

Beleza, pessoal! Quero compartilhar com vocês a aventura que vivi ao lidar com a nova versão do Redis 7 e suas funcionalidades de conexão segura TLS. Fiz algumas modificações no código para torná-lo compatível com essa nova forma de comunicação.

Tudo começou quando a heroku atualizou o Redis para a versão 7, parecia que seria só alegria. Mas aí veio a bomba: a partir desse momento começamos a ter problemas de conexão, nosso código(Java 8) não suportava a tal conexão segura TLS. 

```java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.session.data.redis.config.ConfigureRedisAction;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;


@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)
@Profile("prd")
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {

    @Value("${spring.redis.host}")
    private String host;
	
	@Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.password}")
    private String redisPwd;

    @Bean
    public JedisConnectionFactory connectionFactory() {
        

        final RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName(this.host);
        config.setPort(this.port);
        config.setPassword(RedisPassword.of(this.redisPwd));
        return new JedisConnectionFactory(config);
    }

    @Bean
    public static ConfigureRedisAction configureRedisAction() {
        return ConfigureRedisAction.NO_OP;
    }
}
```

Parecia até que o Redis estava querendo nos fazer passar por alguns perrengues para provar que segurança é coisa séria. Foi aí que vesti minha capa de super-herói e fui em busca da solução.

Então, mergulhei de cabeça na implementação do código. Importei algumas classes importantes para lidar com a conexão segura. 

```java
import redis.clients.jedis.JedisPoolConfig;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
```

No método connectionFactory(), o coração da nossa conexão com o Redis. adicionei as exceções NoSuchAlgorithmException e KeyManagementException para lidar com possíveis erros durante a configuração.

```java
public JedisConnectionFactory connectionFactory() throws NoSuchAlgorithmException, KeyManagementException {
```

Também criei uma instância de SSLContext e a inicializei com null, para que pudesse confiar em todos os certificados e obtive a SSLSocketFactory a partir do SSLContext recém-criado para garantir a segurança na comunicação.

```java
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts(), null);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
```

Em seguida, utilizei o JedisClientConfiguration para configurar a conexão do Jedis com o Redis. Defini opções como tempo limite de leitura e conexão, além de habilitar o uso do SSL.

```java
JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder()
                .usePooling()
                .poolConfig(poolConfig)
                .and()
                .readTimeout(Duration.ZERO)
                .connectTimeout(Duration.ofSeconds(10))
                .useSsl()
                .sslSocketFactory(sslSocketFactory)
                .and()
                .build();
```

Além disso, ajustei a RedisStandaloneConfiguration, adicionando as informações de host, porta e senha do Redis. Por fim, retornei uma nova instância de JedisConnectionFactory, que agora estava configurada para utilizar o Redis com comunicação segura via TLS.

```java
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(this.host, this.port);
redisStandaloneConfiguration.setPassword(RedisPassword.of(this.redisPwd));

return new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
```

Uma outra modificação importante que fiz foi o método trustAllCerts(). implementei o X509TrustManager, que é responsável por gerenciar os certificados. Com isso, pude confiar em todos os certificados durante a comunicação segura.

```java
private TrustManager[] trustAllCerts() {
        return new TrustManager[]{
                new X509TrustManager() {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                    }

                    public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                    }
                }
        };
 }
```

E assim, superamos os problemas de conexão. Agora nossos dados estão protegidos como se estivessem guardados em um cofre de alta segurança.

```java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.session.data.redis.config.ConfigureRedisAction;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;
import redis.clients.jedis.JedisPoolConfig;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;

@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 3600)
@Profile("prd")
public class SessionConfig extends AbstractHttpSessionApplicationInitializer {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    @Value("${spring.redis.password}")
    private String redisPwd;

    @Bean
    public JedisConnectionFactory connectionFactory() throws NoSuchAlgorithmException, KeyManagementException {


        final JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(20);

        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustAllCerts(), null);
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

        JedisClientConfiguration jedisClientConfiguration = JedisClientConfiguration.builder()
                .usePooling()
                .poolConfig(poolConfig)
                .and()
                .readTimeout(Duration.ZERO)
                .connectTimeout(Duration.ofSeconds(10))
                .useSsl()
                .sslSocketFactory(sslSocketFactory)
                .and()
                .build();

        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(this.host, this.port);
        redisStandaloneConfiguration.setPassword(RedisPassword.of(this.redisPwd));

        return new JedisConnectionFactory(redisStandaloneConfiguration, jedisClientConfiguration);
    }

    private TrustManager[] trustAllCerts() {
        return new TrustManager[]{
                new X509TrustManager() {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                    }

                    public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
                    }
                }
        };
    }

    @Bean
    public static ConfigureRedisAction configureRedisAction() {
        return ConfigureRedisAction.NO_OP;
    }
}
```

A moral dessa história é que às vezes as atualizações nos pregam peças, mas com um pouco de dedicação e conhecimento, sempre encontramos uma solução. E agora podemos aproveitar todos os benefícios da comunicação segura TLS no Redis 7.

Espero ter ajudado :)