34

I have implemented some authorization in a webservice that needs be configured.

Currently the user/password combo is hardcoded into the bean configuration. I would like to configure the map with users and passwords into the application.properties so the configuration can be external.

Any clue on how this can be done?

<bean id="BasicAuthorizationInterceptor" class="com.test.BasicAuthAuthorizationInterceptor">
    <property name="users">
        <map>
            <entry key="test1" value="test1"/>
            <entry key="test2" value="test2"/>
        </map>
    </property>
</bean>
166_MMX
  • 590
  • 5
  • 18
Marco
  • 15,101
  • 33
  • 107
  • 174

4 Answers4

87

you can use @Value.

Properties file:

users={test1:'test1',test2:'test2'}

Java code:

@Value("#{${users}}")
private Map<String,String> users;
Marcos Nunes
  • 1,265
  • 12
  • 12
  • 1
    This is good! How can we read map from application.yml file? @Value("#{${users}}") is not working if we use application.yml file instead of application.properties file to read properties – Hemanth Mar 28 '19 at 18:06
  • Lets say I've 50 such properties, so I cant keep adding 50 times `@Value("#{${users}}")`, is there any way if we can read all in place? Or in one go? – PAA Feb 10 '20 at 06:34
  • Lets say I've almost 50 endpoint, I cant keep adding `@Value("#{${users}}")`, I need to read all in one go. – PAA Feb 10 '20 at 07:45
  • Not sure why this answer is accepted. This works like charm in spring-boot world especially – Karthikeyan May 06 '20 at 08:33
  • What if my value is string[] or custom class, how can read? Ex: `private Map users;` or `private Map users;` – Pradeep Dec 14 '20 at 10:04
  • 1
    @Pra_A you can create a bean: `@Bean("users") public Map users(@Value("#{${users}}") Map users) { return users; }` – Thire Feb 11 '21 at 23:23
  • @Pradeep you could inject the Map value into a bean that returns the parsed data (using Jackson or your own logic) – Thire Feb 11 '21 at 23:24
  • @Pradeep You can also explore using `@ConfigurationProperties` – Thire Feb 11 '21 at 23:26
  • Good. Also I had exceptions with injecting my other properties to map in application properties. In that case you can use format: `kafka-topic-by-type={'CRYPTO_PANIC_NEWS': '${kafka.news-topic}'}` – leonaugust Mar 15 '22 at 17:34
55

You can use @ConfigurationProperties to have values from application.properties bound into a bean. To do so you annotate your @Bean method that creates the bean:

@Bean
@ConfigurationProperties
public BasicAuthAuthorizationInterceptor interceptor() {
    return new BasicAuthAuthorizationInterceptor();
}

As part of the bean's initialisation, any property on BasicAuthAuthorizationInterceptor will be set based on the application's environment. For example, if this is your bean's class:

public class BasicAuthAuthorizationInterceptor {

    private Map<String, String> users = new HashMap<String, String>();

    public Map<String, String> getUsers() {
        return this.users;
    }
}

And this is your application.properties:

users.alice=alpha
users.bob=bravo

Then the users map will be populated with two entries: alice:alpha and bob:bravo.

Here's a small sample app that puts this all together:

import java.util.HashMap;
import java.util.Map;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableAutoConfiguration
@EnableConfigurationProperties
public class Application {

    public static void main(String[] args) throws Exception {
        System.out.println(SpringApplication.run(Application.class, args)
                .getBean(BasicAuthAuthorizationInterceptor.class).getUsers());
    }

    @Bean
    @ConfigurationProperties
    public BasicAuthAuthorizationInterceptor interceptor() {
        return new BasicAuthAuthorizationInterceptor();
    }

    public static class BasicAuthAuthorizationInterceptor {

        private Map<String, String> users = new HashMap<String, String>();

        public Map<String, String> getUsers() {
            return this.users;
        }
    }
}

Take a look at the javadoc for ConfigurationProperties for more information on its various configuration options. For example, you can set a prefix to divide your configuration into a number of different namespaces:

@ConfigurationProperties(prefix="foo")

For the binding to work, you'd then have to use the same prefix on the properties declared in application.properties:

foo.users.alice=alpha
foo.users.bob=bravo
Andy Wilkinson
  • 108,729
  • 24
  • 257
  • 242
  • 1
    Note that your bean should have a getter in order to spring be able to populate the map and a public field is not enough. – Meysam Oct 15 '15 at 09:33
  • @MeysaM Have you misread the code? BasicAuthAuthorizationInterceptor already has a `getUsers()` method and the `users` field is `private`, not `public`. – Andy Wilkinson Oct 15 '15 at 09:51
  • 2
    No, It was just a reminder for everyone else to do not repeat my mistake. Your code works absolutely fine. – Meysam Oct 17 '15 at 12:22
  • 1
    This is better than the accepted answer. Just what I was looking for! – bischoje Feb 28 '16 at 19:30
  • `metrics.tagsAsSuffix.jvm.gc=` and `metrics.tagsAsSuffix.jvm.threads=` don't get read into `Map>` with each empty, but `metrics.tagsAsSuffix.jvm` = empty list. The rest of the suffix is dropped – Abhijit Sarkar Jun 09 '18 at 19:58
  • @AndyWilkinson I don't know some how I am getting empty map with the same code. Can you please let me know if anything that I might be missing. Also I have added the same properties in the application.properties as well – Jaini Naveen Apr 29 '20 at 23:41
  • @JainiNaveen There's no way of knowing what you might be missing without seeing some code. Your best bet is to ask a new question with a [minimal, complete, and verifiable example](/help/mcve). – Andy Wilkinson Apr 30 '20 at 09:59
  • What if we want multiple Map.Entry ? – fuat May 03 '20 at 03:04
  • @fuat The code above will create two entries in the map: `alice -> alpha` and `bob -> bravo`. – Andy Wilkinson May 04 '20 at 14:14
7

A java.util.Properties object is already a Map, actually a HashTable which in turn implements Map.

So when you create a properties file (lets name it users.properties) you should be able to load it using a PropertiesFactoryBean or <util:properties /> and inject it into your class.

test1=test1
test2=test2

Then do something like

<util:properties location="classpath:users.properties" id="users" />

<bean id="BasicAuthorizationInterceptor" class="com.test.BasicAuthAuthorizationInterceptor">
    <property name="users" ref="users" />
</bean>

Although if you have a Map<String, String> as a type of the users property it might fail... I wouldn't put them in the application.properties file. But that might just be me..

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
0

I think you are looking for something similar

http://www.codejava.net/frameworks/spring/reading-properties-files-in-spring-with-propertyplaceholderconfigurer-bean

You can pick values from .properties similarly and assign it to your map.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
Paul Phoenix
  • 1,403
  • 6
  • 19
  • 33