Ответ, предоставленный @Sunny Pelletier, работал у меня с мэшапом @ Frank в нашей настройке Java.
Поэтому мне нужно было решение, которое работало бы для нашей локальной настройки докера и для любой из наших сред AWS, в которых есть активные профили и другие переменные окружения, установленные в нашей среде через CDK.
Сначала я начал с простого POJO конфигурации, чтобы настроить свои свойства вне парадигмы spring.data.mongo.*
. Вам не обязательно этого делать, и вы можете просто позволить Spring обработать это, как обычно, для создания файла MongoClient
.
Мой локальный разработчик по умолчанию application.yml
и соответствующий класс конфигурации.
mongo:
user: mongo
password: mongo
host: localhost
port: 27017
database: my-service
@Data
@Configuration
@ConfigurationProperties(prefix = "mongo")
public class MongoConnectConfig {
private int port;
private String host;
private String user;
private String database;
private String password;
}
Затем я создал два AbstractMongoClientConfiguration
дочерних класса; один для местного и один для неместного. Ключевым моментом здесь является то, что я не создавал свой собственный MongoClient
. Причина в том, что мне нужны все хорошие вещи для инициализации Spring Boot, которые вы получаете с фреймворком. Например, авторегистрация всех конвертеров и тому подобное.
Вместо этого я использовал крючок настройки, предоставленный AbstractMongoClientConfiguration.configureClientSettings(MongoClientSettings.Builder builder)
, чтобы затем объединить пользовательские настройки, такие как часть .pem
.
Другая часть заключается в том, что я использовал профили для включения / отключения конфигураций, чтобы сделать их удобными для местных разработчиков; мы не используем никакие профили, кроме default
, для локальной разработки, поэтому проще приступить к настройке, не зная с самого начала.
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@Slf4j
@Configuration
@RequiredArgsConstructor
@Profile({"!dev && !qa && !prod"})
@EnableMongoRepositories(basePackages = "co.my.data.repositories")
public class LocalDevMongoConfig extends AbstractMongoClientConfiguration {
private final MongoConnectConfig config;
@Override
public String getDatabaseName() {
return config.getDatabase();
}
@Override
protected void configureClientSettings(MongoClientSettings.Builder builder) {
log.info("Applying Local Dev MongoDB Configuration");
builder.applyConnectionString(new ConnectionString(getConnectionString()));
}
//mongodb://${mongo.user}:${mongo.password}@${mongo.host}:${mongo.port}/${mongo.database}?authSource=admin
private String getConnectionString() {
return String.format("mongodb://%s:%s@%s:%s/%s?authSource=admin",
config.getUser(),
config.getPassword(),
config.getHost(),
config.getPort(),
config.getDatabase()
);
}
}
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.nio.file.Files;
import java.security.KeyStore;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import java.util.stream.Collectors;
@Slf4j
@Configuration
@RequiredArgsConstructor
@Profile({"dev || qa || prod"})
@EnableMongoRepositories(basePackages = "co.my.data.repositories")
public class DocumentDbMongoConfig extends AbstractMongoClientConfiguration {
private final MongoConnectConfig config;
@Override
public String getDatabaseName() {
return config.getDatabase();
}
@SneakyThrows
@Override
protected void configureClientSettings(MongoClientSettings.Builder builder) {
log.info("Applying AWS DocumentDB Configuration");
builder.applyConnectionString(new ConnectionString(getConnectionString()));
var endOfCertificateDelimiter = "-----END CERTIFICATE-----";
File resource = new ClassPathResource("certs/rds-combined-ca-bundle.pem").getFile();
String pemContents = new String(Files.readAllBytes(resource.toPath()));
var allCertificates = Arrays.stream(pemContents
.split(endOfCertificateDelimiter))
.filter(line -> !line.isBlank())
.map(line -> line + endOfCertificateDelimiter)
.collect(Collectors.toUnmodifiableList());
var certificateFactory = CertificateFactory.getInstance("X.509");
var keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
// This allows us to use an in-memory key-store
keyStore.load(null);
for (int i = 0; i < allCertificates.size(); i++) {
var certString = allCertificates.get(i);
var caCert = certificateFactory.generateCertificate(new ByteArrayInputStream(certString.getBytes()));
keyStore.setCertificateEntry(String.format("AWS-certificate-%s", i), caCert);
}
var trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
var sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
builder.applyToSslSettings(ssl -> {
ssl.enabled(true).context(sslContext);
});
}
/**
* Partly based on the AWS Console "Connectivity & security " section in the DocumentDB Cluster View.
* Since we register the pem above, we don't need to add the ssl & sslCAFile piece
* mongodb://${user}:${password}@${host}:${port}/?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false
*/
private String getConnectionString() {
return String.format("mongodb://%s:%s@%s:%s/%s?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false",
config.getUser(),
config.getPassword(),
config.getHost(),
config.getPort(),
config.getDatabase()
);
}
}
Наконец, мы помещаем rds-combined-ca-bundle.pem
в папку src/main/resources/certs/
.
Примечания на стороне:
- Опять же, я считаю, что вы можете обойтись без использования свойств
spring.data*
по умолчанию, и ваш MongoClient
должен был их использовать.
- Не обращайте внимания на
@SneakyThrows
здесь, я сделал это для краткости кода, обрабатывайте отмеченные исключения так, как считаете нужным.
- Думаю, мы можем понять, почему синтаксис Kotlin можно считать более чистым, да? :)
person
Hermann Steidel
schedule
15.07.2021