1

1) I created environment file as mentioned here

  • I created a file called Prod.env and entered following

    SPRING_DATASOURCE_URL="jdbc:mysql://5.6.7.8:3306/ab?autoReconnect=true&characterEncoding=utf8"
    SPRING_DATASOURCE_USERNAME="root"
    SPRING_DATASOURCE_PASSWORD="IWin"
    
  • And then I executed this command export $(cat Prod.env | xargs)

2) Then I created application.properties under WEB-INF with following code

spring.datasource.driver-class-name:com.mysql.jdbc.Driver
spring.datasource.url=${SPRING_DATASOURCE_URL}
spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}

3) Then in spring-security.xml

  <b:bean id="mappings" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <b:property name="location">
            <b:value>/WEB-INF/application.properties</b:value>
        </b:property>
  </b:bean> 
  <b:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <b:property name="driverClassName" value="com.mysql.jdbc.Driver" /> 
        <b:property name="url" value="${spring.datasource.url}" />
        <b:property name="username" value="${spring.datasource.username}" />
        <b:property name="password" value="${spring.datasource.password}" />
  </b:bean>

Now, when I save everything and restart I get

SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'dataSource' defined in ServletContext resource [/WEB-INF/spring/appServlet/spring-security.xml]: Could not resolve placeholder 'SPRING_DATASOURCE_URL' in string value "jdbc:mysql:${SPRING_DATASOURCE_URL}"; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'SPRING_DATASOURCE_URL' in string value "jdbc:mysql:${SPRING_DATASOURCE_URL}"

What I'm trying to achieve is: Access an environment variable via application.properties (or doing some configuration in spring-security.xml ) , but not with java code. What am I missing here?

I have searched several questions, but none explains it clearly. Please help

Note: Mine is not a spring boot project. It is normal Spring 4.0.3 web app.

LMC
  • 10,453
  • 2
  • 27
  • 52
sofs1
  • 3,834
  • 11
  • 51
  • 89
  • May be you already know, `export` command must be run every time you open a terminal. To persist those env vars, add them to .profile or .bashrc. And saving a password in clear text to env vars or to a plain text file it's a big security issue. – LMC Aug 17 '19 at 23:11
  • @LuisMuñoz So how to set env vars without entering password in a text file. If I use hashed passwords and put it in a text file and then set enn vars, each time I use the encrypted password, i have to decrypt it. Is there any other options than these? – sofs1 Aug 17 '19 at 23:16
  • http://www.jasypt.org/spring3.html looks like a good option. – LMC Aug 17 '19 at 23:25
  • Ok. I would like to tackle on problem at a time. I first plan to add env variables to bash shell so that it is added permanently and I don't have to set it each time. To do that I'm following this https://unix.stackexchange.com/questions/117467/how-to-permanently-set-environmental-variables --> I open /etc/profile and added `export $(cat Prod.env | xargs)` as last line and saved and restarted bash. I don't see those vars there. How do I permanently set env vars so that I don't have to run export command each time I open terminal – sofs1 Aug 17 '19 at 23:46
  • Better do that on your home profile `$HOME/.profile`. The other way password is available to every user ;). Just add one `export` per var, without the cat and stuff. Then run `source $HOME/.profile` or login out and in. – LMC Aug 18 '19 at 00:17
  • oK i tried adding env vars in ~/.profile as well as ~/.bash_profile . I notice that added env vars when I type `env`, but spring app still doesn't pick it up. – sofs1 Aug 18 '19 at 01:16

1 Answers1

0

The normal approach is that the application should not know anything about the environment type (ENV, INTP, PROD, etc.).It means, do not specify environment (stage) name, but use the same name of properties file, an put needed version of the file into the classpath. For instance, on DEV environment you put to the classpath the DEV version of application.properties, on PROD environment you put to the classpath the PROD version of application.properties.

In your case I'd suggest that you remove application.properties from WEB-INF and put it outside of WAR (if you use EAR, also outside of EAR). Put it to the classpath of your application or classpath of application server, and configure your bean as follows:

  <b:bean id="mappings" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <b:property name="location">
            <b:value>classpath*:application.properties</b:value>
        </b:property>
  </b:bean> 

Property spring.datasource.driver-class-name it is not needed in the properties file, because you have already defined it in XML directly:

<b:property name="driverClassName" value="com.mysql.jdbc.Driver" /> 

Other properties you can define in application.properties as follows:

spring.datasource.url="jdbc:mysql://5.6.7.8:3306/ab?autoReconnect=true&amp;characterEncoding=utf8"
spring.datasource.username=root
spring.datasource.password=IWin

Put application.properties not into WAR, but to the classpath of your app server.

mentallurg
  • 4,967
  • 5
  • 28
  • 36
  • 1) so, don't I need datasource bean at all ? 2) What If I don't want to have password unencrypted in application.properties lying in classpath ? 3) How do identify my classpath and place application.properties ? – sofs1 Aug 18 '19 at 00:03
  • 1) I have extended the answer. 2) The password is more safe when it is in the classpath of the app server, because only server admins have access to it. Where as if you include password into the application, you have to know it much earlier, when you build the application. That's why much more people will have access to your password. – mentallurg Aug 18 '19 at 00:13
  • You can make password not so easy readable. But remember, that any one who has access to the server and has the same permission as your application will be able to read / decrypt the them in the same way as the application. Nevertheless it makes sense to at least make the password no so easy readable for people who have no access to the server. One possible approach can be to use Spring vault. See more details here - https://projects.spring.io/spring-vault/, or here - https://spring.io/blog/2016/06/24/managing-secrets-with-vault. – mentallurg Aug 18 '19 at 00:19
  • 1
    To 3): The classpath depends on your application server. For instance, for WebSphere it is PROFILE_ROOT/properties, for Tomcat you can configure classpath in catalina.properties. – mentallurg Aug 18 '19 at 00:23