Unable to decrypt error using jasypt with spring boot - spring-boot

I am using spring boot:2.2.2.RELEASE when i tried to add jasypt functionality to hide my password i got the following error
Unable to decrypt: ENC(MyEncryptedPass). Decryption of Properties failed, make sure encryption/decryption passwords match
i used the command line to encrypt the password and decrypt it and it works fine so i am sure my encryption and decryption passwords are exact but i get this error when i try to launch my spring application. So any help (•–•)

As from version 3.0.0 of jasypt-spring-boot, the default encryption/decryption algorithm has changed to PBEWITHHMACSHA512ANDAES_256
The change can be found here: https://github.com/ulisesbocchio/jasypt-spring-boot#update-11242019-version-300-release-includes
To decrypt previously encrypted values, add these two values in your properties:
jasypt.encryptor.algorithm=PBEWithMD5AndDES
jasypt.encryptor.iv-generator-classname=org.jasypt.iv.NoIvGenerator

I was also facing the same issue. Initially, I was encrypting using jasypt CLI and putting the same value in the property file. But by default property of com.github.ulisesbocchio jar is different from CLI. Try to use the below code for encryption.
private static StringEncryptor stringEncryptor() {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(password);
config.setAlgorithm("PBEWithMD5AndDES");
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setProviderName("SunJCE");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
return encryptor;
}
private static String encrypt(String text) {
StringEncryptor textEncryptor = stringEncryptor();
String encryptedText = textEncryptor.encrypt(text);
return encryptedText;
}
private static String decrypt(String text) {
StringEncryptor textEncryptor = stringEncryptor();
String decryptedText = textEncryptor.decrypt(text);
return decryptedText;
}
public static void main(String[] args) {
System.out.println(encrypt("StackOverFlow"));
}

Ok the solution was to add this configuration to my project.properties file
jasypt.encryptor.iv-generator-classname=org.jasypt.iv.NoIvGenerator
cause probably i am using algorithm = PBEWithMD5AndDES which doesn't require initialization vector. But of course that's just my explanation which doesn't mean anything :''D.

Related

How to configure Firestore without using .json file in Spring Boot?

I'm using Spring boot 2.7 and firebase-admin 7.1.0 dependency. I need to create a Firestore bean using Firestore credentials. I didn't find any way to set credentials using application.properties or by setting the environment variables. The only way I found was to get credentials using a .json file. I feel this is not secure.
#Value("${firebase.credential.path}")
private String credentialPath;
#Bean
public Firestore getFireStore() throws IOException {
FileInputStream serviceAccount = null;
try {
serviceAccount = new FileInputStream(credentialPath);
var credentials = GoogleCredentials.fromStream(serviceAccount);
var options = FirestoreOptions.newBuilder()
.setCredentials(credentials).build();
return options.getService();
} catch (IOException e) {
log.error("Error in firebase authentication bean: {}", e.getMessage());
return null;
}
}
Is there any way to set credentials in the application.properties file or use environment variables if not Is it secure to take credentials from a separate .json file?
I dont no if thats a good way to solve this, but its working for me:
#Bean
fun provideFirebaseOptions(): FirebaseApp {
val jsonObject = JsonObject()
jsonObject.addProperty("type", type)
jsonObject.addProperty("project_id", projectId)
jsonObject.addProperty("private_key_id", privateKeyId)
// to get linebreaks inside env variable
jsonObject.addProperty("private_key", privateKey.replace("\\n", "\n"))
jsonObject.addProperty("client_email", clientEmail)
jsonObject.addProperty("client_id", clientId)
jsonObject.addProperty("auth_uri", authUri)
jsonObject.addProperty("token_uri", tokenUri)
jsonObject.addProperty("auth_provider_x509_cert_url", authProviderX509CertUrl)
jsonObject.addProperty("client_x509_cert_url", clientX509CertUrl)
val inputStream = ByteArrayInputStream(jsonObject.toString().toByteArray())
val options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(inputStream))
.build()
return FirebaseApp.initializeApp(options)
}
Maybe you dont need .replace("\\n", "\n")) for the private key. I had some problems with linebreaks inside environment variables.

Spring-ws security header to load configurations for multiple cert

I am trying to invoke a SOAP webservice in my spring boot application using spring-ws with a keystore which has multiple certs. The configuration always defaults to single cert.
Sample code below:
Wss4jSecurityInterceptor wss4jSecurityInterceptor = new Wss4jSecurityInterceptor();
Merlin merlin = new Merlin();
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
InputStream inputStream = new FileInputStream(ResourceUtils.getFile(("keystore.jks")));
keyStore.load(inputStream, "tester".toCharArray());
merlin.setKeyStore(keyStore);
wss4jSecurityInterceptor.setSecurementSignatureCrypto(merlin);
wss4jSecurityInterceptor.setSecurementUsername("test");
wss4jSecurityInterceptor.setSecurementPassword("");
webServiceTemplate.setInterceptors(new org.springframework.ws.client.support.interceptor.ClientInterceptor[]
{wss4jSecurityInterceptor});
When i checked the source code of the apache library class WSSecSignature class. I see there is a configuration for picking up multiple cert. But am not sure how to set the singleCert to be false in the wss4jSecurityInterceptor. It always goes to the else block in the below logic
if (!this.useSingleCert) {
this.secRef.addTokenType("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1");
ref.setValueType("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1");
} else {
ref.setValueType("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3");
}
Is there a config i need to set while setting the keystore to Merin object, to make the useSingleCert as false?
Found a work around to override the Wss4jSecurityInterceptor, set the property to false and use the extended interceptor
class SecurityInterceptor extends Wss4jSecurityInterceptor
{
#Override
protected RequestData initializeRequestData(MessageContext messageContext) {
messageContext.setProperty(WSHandlerConstants.USE_SINGLE_CERTIFICATE, "false");
return super.initializeRequestData(messageContext);
}
}

How to replace the static keystore with a dynamic one for a spring-boot-application

I want to add/replace SSL certificates dynamically to my spring boot (tomcat) application without the need to restart it. I have a long way to go still, but currently I am stuck with a javax.crypto.BadPaddingException and don't know why.
So here is what I am trying to do.
First, I am defining my own TomcatServletWebServerFactoryin order to set a SslStoreProvider.
#Component
public class PathWatchingTomcatFactory extends TomcatServletWebServerFactory {
public PathWatchingTomcatFactory(PathWatchingSslStoreProvider pathWatchingSslStoreProvider) {
setSslStoreProvider(pathWatchingSslStoreProvider);
}
}
My PathWatchingSslStoreProvider provides a PathMatchingKeyStore.
#Component
public class PathWatchingSslStoreProvider implements SslStoreProvider {
private final PathWatchingKeyStore pathWatchingKeyStore;
public PathWatchingSslStoreProvider(PathWatchingKeyStore pathWatchingKeyStore) {
this.pathWatchingKeyStore = pathWatchingKeyStore;
}
#Override
public KeyStore getKeyStore() throws Exception {
return pathWatchingKeyStore;
}
}
The PathWatchingKeyStore seems only necessary in order to provide a service provider interface to it.
#Component
public class PathWatchingKeyStore extends KeyStore {
protected PathWatchingKeyStore(
PathWatchingKeyStoreSpi pathWatchingKeyStoreSpi,
DynamicProvider provider)
{
super(pathWatchingKeyStoreSpi, provider, KeyStore.getDefaultType());
initialize();
}
private void initialize() {
// Loading a keystore marks it internally as initialized and only
// initialized keystores work properly. Unfortunately
// nobody initializes this keystore. So we have to do it
// ourselves.
//
// Internally the keystore will delegate loading to the
// KeyStoreSpi, which, in our case is the PathWatchingKeyStoreSpi.
try {
load(null, null);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Now, on startup, the keystore will be loaded. And because I provide a SslStoreProvider my keystore will be loaded by the SslStoreProviderUrlStreamHandlerFactory by requesting my PathWatchingKeyStoreSpi to store its keystore into a ByteArrayOutputStream whose content is finally copied into the InputStream that is used to load an internally used keystore.
In the following code snippet you can see how I try to write the contents of an already existing keystore. No dynamic at all right now. I only want to see if the spring boot application starts with all these custom classes in place. But it doesn't.
#Component
public class PathWatchingKeyStoreSpi extends KeyStoreSpi {
private static final Logger LOGGER = LoggerFactory.getLogger(PathWatchingKeyStoreSpi.class);
private final Path keyStoreLocation;
public PathWatchingKeyStoreSpi(#Value("${server.ssl.key-store}") Path keyStoreLocation) {
super();
this.keyStoreLocation = keyStoreLocation;
}
#Override
public void engineStore(OutputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException {
try {
final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(keyStoreLocation.toString()), "secret".toCharArray());
// Password must be empty because the SslConnectorCustomizer sets the keystore
// password used by the tomcat to the empty string if the SslStoreProvider
// returns a keystore. And because that is what we wanted to do in the first place,
// providing a dynamic keystore, this is what we have to do.
keyStore.store(stream, "".toCharArray());
}
catch (Exception e) {
e.printStackTrace();
}
}
}
I can see that the keystore is loaded but as soon as the SSLUtilBase tries to read the key from that store, it throws a BadPaddingException:
Caused by: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
at java.base/com.sun.crypto.provider.CipherCore.unpad(CipherCore.java:975) ~[na:na]
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1056) ~[na:na]
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853) ~[na:na]
at java.base/com.sun.crypto.provider.PKCS12PBECipherCore.implDoFinal(PKCS12PBECipherCore.java:408) ~[na:na]
at java.base/com.sun.crypto.provider.PKCS12PBECipherCore$PBEWithSHA1AndDESede.engineDoFinal(PKCS12PBECipherCore.java:440) ~[na:na]
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2202) ~[na:na]
at java.base/sun.security.pkcs12.PKCS12KeyStore.lambda$engineGetKey$0(PKCS12KeyStore.java:406) ~[na:na]
at java.base/sun.security.pkcs12.PKCS12KeyStore$RetryWithZero.run(PKCS12KeyStore.java:302) ~[na:na]
at java.base/sun.security.pkcs12.PKCS12KeyStore.engineGetKey(PKCS12KeyStore.java:400) ~[na:na]
... 25 common frames omitted
I created the static keystore I am using here as follows:
keytool -genkey -alias tomcat -keyalg RSA
First of all, is the direction I am going to solve my problem promising? Or am I totally wrong? I first tried to only inject my own X509ExtendedKeyManager. I could see in the debugger that it is the key manager that is asked for a certificate for an incoming request but nonetheless the tomcat endpoint seems to be initialized with a keystore without the manager being involved.
Has anybody ever tried to implement and use a dynamic keystore/trustore for a spring boot application using tomcat as servelt container?
Any help is welcome.
Tobias
Ok, I don't know if this is final solution but right now it seems a lot more promising (and less complex) then my first way described above.
Again it all starts with the TomcatServletWebServerFactory. But this time I set a completely new JSSEImplementation:
#Component
public class PathWatchingTomcatFactory extends TomcatServletWebServerFactory {
private final Path keysLocation;
public PathWatchingTomcatFactory(#Value("${tobias.spring.ssl.keys-location}")Path keysLocation) {
this.keysLocation = requireNonNull(keysLocation);
}
#Override
protected void customizeConnector(Connector connector) {
super.customizeConnector(connector);
connector.setProperty("sslImplementationName", DynamicSslImplementation.class.getName());
System.setProperty("tobias.spring.ssl.keys-location", keysLocation.toUri().toString());
}
}
The implementation class is very simple. It only has to provide a custom SSLUtil instance.
public class DynamicSslImplementation extends JSSEImplementation {
public DynamicSslImplementation() {
super();
}
#Override
public SSLUtil getSSLUtil(SSLHostConfigCertificate certificate) {
return new DynamicSslUtil(certificate);
}
}
And the SSLUtil instance provides my own PathWatchingKeyManager, which will return keys from a certain directory.
public class DynamicSslUtil extends JSSEUtil {
DynamicSslUtil(SSLHostConfigCertificate certificate) {
super(certificate);
}
#Override
public KeyManager[] getKeyManagers() {
return new KeyManager[]{new DynamicKeyManager()};
}
}
The server.ssl.key-storeproperty must be set to NONE.
This seems to work. The spring boot applications starts running without failures and the DynamicKeyManager is asked for a certificate for a https request.
If this will work indeed, I will post the complete solution here.

How to diagnose/fix Signature Digest Verification Failure on web service response?

I am using wss4jSecurityInterceptor for validation and securement actions on my webservice. The SOAP messages back and forth are secured with Signature, Encryption and Timestamp. The problem I am having is, the recipient is unable to verify my response signature and gets the following error:
oracle.wsm.security.SecurityException: WSM-00061 : Signature digest verification failure. The system property xml.debug.verify should be enabled for the details about the digest calculations during verification phase (note xml.debug.verify slows down the signature verification for very large messages).
Here is the code that I am using to perform validation and securement actions:
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
private static Log log = LogFactory.getLog(WebServiceConfig.class);
#Bean
public KeyStoreCallbackHandler securityCallbackHandler() {
KeyStoreCallbackHandler callbackHandler = new KeyStoreCallbackHandler();
callbackHandler.setPrivateKeyPassword("changeit");
return callbackHandler;
}
#Bean
public Wss4jSecurityInterceptor securityInterceptor() throws Exception {
Wss4jSecurityInterceptor securityInterceptor = new Wss4jSecurityInterceptor();
// validate incoming request
securityInterceptor.setValidationActions("Timestamp Signature Encrypt");
securityInterceptor.setValidationSignatureCrypto(getCryptoFactoryBean().getObject());
securityInterceptor.setValidationDecryptionCrypto(getCryptoFactoryBean().getObject());
securityInterceptor.setValidationCallbackHandler(securityCallbackHandler());
// encrypt the response
securityInterceptor.setSecurementEncryptionUser("client_pub"); // alias of client public key
securityInterceptor.setSecurementEncryptionParts("{Content}{}Body");
securityInterceptor.setSecurementEncryptionKeyIdentifier("DirectReference");
securityInterceptor.setSecurementEncryptionCrypto(getCryptoFactoryBean().getObject());
// sign the response
securityInterceptor.setSecurementSignatureAlgorithm(WSS4JConstants.RSA_SHA1);
securityInterceptor.setSecurementSignatureDigestAlgorithm(WSS4JConstants.SHA1);
securityInterceptor.setSecurementSignatureKeyIdentifier("DirectReference");
securityInterceptor.setSecurementActions("Encrypt Signature Timestamp");
securityInterceptor.setSecurementUsername("secret"); // alias of the private key
securityInterceptor.setSecurementPassword("changeit");
securityInterceptor.setSecurementSignatureCrypto(getCryptoFactoryBean().getObject());
return securityInterceptor;
}
#Bean
public CryptoFactoryBean getCryptoFactoryBean() throws IOException {
CryptoFactoryBean cryptoFactoryBean = new CryptoFactoryBean();
cryptoFactoryBean.setKeyStorePassword("changeit");
cryptoFactoryBean.setKeyStoreLocation(new ClassPathResource("cert/myKeystore.jks"));
return cryptoFactoryBean;
}
#Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
try {
interceptors.add(securityInterceptor());
} catch (Exception e) {
throw new RuntimeException("could not initialize security interceptor");
}
}
#Bean
public ServletRegistrationBean<MessageDispatcherServlet> registerMessageDispatcherServlet(
ApplicationContext context) {
MessageDispatcherServlet messageDispatcherServlet = new MessageDispatcherServlet();
messageDispatcherServlet.setApplicationContext(context);
messageDispatcherServlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<MessageDispatcherServlet>(messageDispatcherServlet, "/ws/*");
}
The receiving side suspects that the error is caused by the fact that the certificate sent as has been corrupted by carriage return character sequence. Here is what it looks like:
<wsse:BinarySecurityToken
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
wsu:Id="X509-124b3a03-73e3-4838-a254-f20883ff4184">MIIG1zCCBb+gAwIBAgIRAIVi4APh2tBtAAAAAFDvSXwwDQYJKoZIhvcNAQELBQAwgboxCzAJBgNV
BAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3Qu
 bmV0L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1
 dGhvcml6ZWQgdXNlIG9ubHkxLjAsBgNVBAMTJUVudHJ1c3QgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkgLSBMMUswHhcNMTkwMzIyMTY1ODA5WhcNMjAwMzIyMTcyODA4WjCBgjELMAkGA1UEBhMCVVMx
 EzARBgNVBAgTCkNhbGlmb3JuaWExETAPBgNVBAcTCEJlcmtlbGV5MS4wLAYDVQQKEyVMYXdyZW5j
 ZSBCZXJrZWxleSBOYXRpb25hbCBMYWJvcmF0b3J5MRswGQYDVQQDExJtc2cudWNwLWl0LmxibC5n
b3YwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9fSfO1cKz1D34dQaQF1ScY0eEGALm
 s3hkRRWMYPWTH8gZRpfP4iFLF9Sx09vagGFHUrru1xf8BnVyB2CsZnvHtE+bDrK4kp4hQpO8aXqf
 VEpn+SpY38aQDfZUqjRK6HJM5qxF/2xjNKSw41MPkXALK04yga07IwSUE+GpPt8i2noQPKKJufzf
guA8Plmsnf9uNcpxFezNXC1k+HQBKeO0V+CZ4K5FKyckq64eT8VWbawF5CFoNigtbxTuHHClECV0
 VrzNNf5jj/cpymwOu51ljnPhGEY/f73DlEBZg8jpFcDSnAyYyJH2GFgz2wa0TiOpdxItTWHMXalb
 YtDH9VvRAgMBAAGjggMMMIIDCDAdBgNVHREEFjAUghJtc2cudWNwLWl0LmxibC5nb3YwggF/Bgor
BgEEAdZ5AgQCBIIBbwSCAWsBaQB2AFWB1MIWkDYBSuoLm1c8U/DA5Dh4cCUIFy+jqh0HE9MMAAAB
 aaZyHvYAAAQDAEcwRQIhAJtC0LBFOfupRDy6BOvRrH6pAlvAJFFUWxMbbSRV59nOAiB/c2D5VOwS
 aBeC4czjDjUAAfeunN/npVLyX7i6v69aLgB3AId1v+dZfPiMQ5lfvfNu/1aNR1Y2/0q1YMG06v9e
oIMPAAABaaZyHwQAAAQDAEgwRgIhAJj/g/g+UjWaoHCl7ldHG08zuUrL8XbAkR8Op4G+//nvAiEA
 sLoq29TrwnOtWa+O1+PRHCCllKD22yeKxdMUNrR2Pu0AdgCyHgXMi6LNiiBOh2b5K7mKJSBna9r6
 cOeySVMt74uQXgAAAWmmch7YAAAEAwBHMEUCIBVGxbR2c/ByOtfDRMlpIFPRYA5+dGkB4zJ7tjQ6
moYYAiEAmFbeJdg9rCZt6qUqhRH2mxJlotRjp+mZmiHyRFmulykwDgYDVR0PAQH/BAQDAgWgMB0G
 A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js
 LmVudHJ1c3QubmV0L2xldmVsMWsuY3JsMEsGA1UdIAREMEIwNgYKYIZIAYb6bAoBBTAoMCYGCCsG
AQUFBwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L3JwYTAIBgZngQwBAgIwaAYIKwYBBQUHAQEE
 XDBaMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBggrBgEFBQcwAoYnaHR0
 cDovL2FpYS5lbnRydXN0Lm5ldC9sMWstY2hhaW4yNTYuY2VyMB8GA1UdIwQYMBaAFIKicHTdvFM/
z3vU981/p2DGCky/MB0GA1UdDgQWBBRzakStBRG3O6NBdLt1XUkvwwKn4jAJBgNVHRMEAjAAMA0G
 CSqGSIb3DQEBCwUAA4IBAQAqt00w3oV4XgcdhcanJdgY/SUCtmN9I4jdras8vRson+wK6WJy6Em8
 EF5wyRDLOwUD75Rm9Ak23lKjYOcDTWQGG3YXrYh7xe3J8C+plEY1NAwNPXflCzxkDcdJ4pX0LDfj
aC5FniAvKJ9ET7ZQvpCjBRd7wgOrkuhf3YjCFC3hM4E6D7cGb6DEhCh9nOy+e9woQ/C1LnDil3NX
 Vgqk3PMZYkUeHjxqItnDcOAeJaeqsAUiTxZbC8sQQ9ae/CPahghgSWEwL7tMrAH4nGT3yeBQl0I9
 O7H9xWj0ooQ8Wt6nO7pq64X5uc7W6iA3/1Of5uCntmMrsqPlvSscP6QVuPw6</wsse:BinarySecurityToken>
My keystore is in PKCS format and I have also converted to jks. No dice. The certs in the keystore verify just fine with openssl command, so I know they are good.
I do my builds on a mac and deploy on CentOS. I have also copied the sources to CentOS and run the build there. Again, made no difference.
Has anyone run into this issue and solved it? Would appreciate any help!
Thanks much!
xmlsec library upgraded to 2.1.3.
wss4j-ws-security-dom upgraded to 2.2.3
Details: The problem was narrow and very obscure. I haven't put everything I did back to the way it was to see if the problem returns, but could do that easily. We use spring-ws-security and WSS4Jfrom apache to perform the validation and securement actions. Both libraries include xmlsec.jar which provides this functionality. In fact wss4j includes it twice since it also embeds opensaml , which in turn contains xmlsec. Spring-ws-security also contains its own xmlsec. They are all different versions. The problem with carriage returns is reported for xmlsec versions 2.1.0 and above.
https://issues.apache.org/jira/browse/SANTUARIO-482
https://issues.apache.org/jira/browse/SANTUARIO-494
I updated wss4j to ver 2.2.3 (where this is ostensibly fixed) and also added the fixed version of xmlsec.jar directly for good measure.
Hope it helps someone else out. :-)

Spring Boot and how to configure connection details to MongoDB?

Being new to Spring Boot I am wondering on how I can configure connection details for MongoDB.
I have tried the normal examples but none covers the connection details.
I want to specify the database that is going to be used and the url/port of the host that runs MongoDB.
Any hints or tips?
Just to quote Boot Docs:
You can set spring.data.mongodb.uri property to change the url, or alternatively specify a host/port. For example, you might declare the following in your application.properties:
spring.data.mongodb.host=mongoserver
spring.data.mongodb.port=27017
All available options for spring.data.mongodb prefix are fields of MongoProperties:
private String host;
private int port = DBPort.PORT;
private String uri = "mongodb://localhost/test";
private String database;
private String gridFsDatabase;
private String username;
private char[] password;
It's also important to note that MongoDB has the concept of "authentication database", which can be different than the database you are connecting to. For example, if you use the official Docker image for Mongo and specify the environment variables MONGO_INITDB_ROOT_USERNAME and MONGO_INITDB_ROOT_PASSWORD, a user will be created on 'admin' database, which is probably not the database you want to use. In this case, you should specify parameters accordingly on your application.properties file using:
spring.data.mongodb.host=127.0.0.1
spring.data.mongodb.port=27017
spring.data.mongodb.authentication-database=admin
spring.data.mongodb.username=<username specified on MONGO_INITDB_ROOT_USERNAME>
spring.data.mongodb.password=<password specified on MONGO_INITDB_ROOT_PASSWORD>
spring.data.mongodb.database=<the db you want to use>
spring.data.mongodb.host and spring.data.mongodb.port are not supported if you’re using the Mongo 3.0 Java driver. In such cases, spring.data.mongodb.uri should be used to provide all of the configuration, like this:
spring.data.mongodb.uri=mongodb://user:secret#mongo1.example.com:12345
In a maven project create a file src/main/resources/application.yml with the following content:
spring.profiles: integration
# use local or embedded mongodb at localhost:27017
---
spring.profiles: production
spring.data.mongodb.uri: mongodb://<user>:<passwd>#<host>:<port>/<dbname>
Spring Boot will automatically use this file to configure your application. Then you can start your spring boot application either with the integration profile (and use your local MongoDB)
java -jar -Dspring.profiles.active=integration your-app.jar
or with the production profile (and use your production MongoDB)
java -jar -Dspring.profiles.active=production your-app.jar
You can define more details by extending AbstractMongoConfiguration.
#Configuration
#EnableMongoRepositories("demo.mongo.model")
public class SpringMongoConfig extends AbstractMongoConfiguration {
#Value("${spring.profiles.active}")
private String profileActive;
#Value("${spring.application.name}")
private String proAppName;
#Value("${spring.data.mongodb.host}")
private String mongoHost;
#Value("${spring.data.mongodb.port}")
private String mongoPort;
#Value("${spring.data.mongodb.database}")
private String mongoDB;
#Override
public MongoMappingContext mongoMappingContext()
throws ClassNotFoundException {
// TODO Auto-generated method stub
return super.mongoMappingContext();
}
#Override
#Bean
public Mongo mongo() throws Exception {
return new MongoClient(mongoHost + ":" + mongoPort);
}
#Override
protected String getDatabaseName() {
// TODO Auto-generated method stub
return mongoDB;
}
}
In case that somebody is trying to connect to a Atlas MongoDB Cluster in application.properties has to have a config like:
spring.data.mongodb.uri=mongodb+srv://databaseUsername:usernamePassword#cluster0.j4koa.mongodb.net/databaseUsername?retryWrites=true&w=majority
In my case I needed to set up MongoDB for integration tests using Testcontainers. Using properites file was not an option since Mongo port had to be specified during runtime. I wanted to preseve original MongoDB autoconfiguration provided by SpringBoot but override some of the properties. This can be achieved by defining a bean of type MongoClientSettingsBuilderCustomizer which can be used to customize mongo settings :
#Bean
public MongoClientSettingsBuilderCustomizer clientSettingsBuilderCustomizer(final GenericContainer<?> mongoDBContainer) {
String database = environment.getProperty("spring.data.mongodb.database");
ConnectionString connectionString = new ConnectionString(String.format("mongodb://localhost:%s/%s", mongoDBContainer.getFirstMappedPort(), database));
return settings -> settings.applyConnectionString(connectionString);
}
If you simply want to read a MongoDB connection string from an environment variable, one way is to set the following environment variable:
SPRING_DATA_MONGODB_URI=mongodb://localhost:27017/trying-mongo
This doesn't require any changes in the application.properties as the spring data mongo will read the value from the above environment variable by default.
Here is How you can do in Spring Boot 2.0 by creating custom MongoClient adding Providing more control for Connection ,
Please follow github Link for Full Source Code
#Configuration
#EnableMongoRepositories(basePackages = { "com.frugalis.repository" })
#ComponentScan(basePackages = { "com.frugalis.*" })
#PropertySource("classpath:application.properties")
public class MongoJPAConfig extends AbstractMongoConfiguration {
#Value("${com.frugalis.mongo.database}")
private String database;
#Value("${com.frugalis.mongo.server}")
private String host;
#Value("${com.frugalis.mongo.port}")
private String port;
#Value("${com.frugalis.mongo.username}")
private String username;
#Value("${com.frugalis.mongo.password}")
private String password;
#Override
protected String getDatabaseName() {
return database;
}
#Override
protected String getMappingBasePackage() {
return "com.frugalis.entity.mongo";
}
#Bean
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoClient(), getDatabaseName());
}
#Override
#Bean
public MongoClient mongoClient() {
List<MongoCredential> allCred = new ArrayList<MongoCredential>();
System.out.println("???????????????????"+username+" "+database+" "+password+" "+host+" "+port);
allCred.add(MongoCredential.createCredential(username, database, password.toCharArray()));
MongoClient client = new MongoClient((new ServerAddress(host, Integer.parseInt(port))), allCred);
client.setWriteConcern(WriteConcern.ACKNOWLEDGED);
return client;
}}

Resources