I want to escape my Spring propeties file in order to get in my bean property: ${ROOTPATH}/relativePath
I have a simple Spring config file that contains:
<context:property-placeholder location="classpath:myprops.properties" />
<bean id="myBean" class="spring.MyBean">
<property name="myProperty" value="${myproperty}" />
</bean>
The myprops.properties contains:
myproperty=\${ROOTPATH}/relativePath
The above setup returns: Could not resolve placeholder 'ROOTPATH'. I tried a lot of possible syntaxes but was not able to find the right one.
Instead of ${myproperty} use #{'$'}{myproperty}. Simply replace $ with #{'$'}.
Seems so far, that is no way to escape the ${}, however you can try below configuration to solve the problem
dollar=$
myproperty=${dollar}{myproperty}
Result for myproperty will be ${myproperty} after evaluation.
Here is a Spring ticket which asks for escaping support (still unresolved at the time of writing).
The workaround of using
$=$
myproperty=${$}{ROOTPATH}/relativePath
does provide a solution, but looks quite dirty.
Using SPEL expressions like #{'$'} did not work for me with Spring Boot 1.5.7.
Although it works, escaping the placeholder is super-ugly.
I achieved this my overriding PropertySourcesPlaceholderConfigurer.doProcessProperties and using a custom StringValueResolver
public static class CustomPropertySourcesPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer {
#Override
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess, StringValueResolver valueResolver) {
StringValueResolver customValueResolver = strVal -> {
if(strVal.startsWith("${something.")) {
PropertySourcesPropertyResolver customPropertySourcesPropertyResolver = new PropertySourcesPropertyResolver(this.getAppliedPropertySources());
String resolvedText = customPropertySourcesPropertyResolver.resolvePlaceholders(strVal);
//remove the below check if you are okay with the property not being present (i.e remove if the property is optional)
if(resolvedText.equals(strVal)) {
throw new RuntimeException("placeholder " + strVal + " not found");
}
return resolvedText;
}
else {
//default behaviour
return valueResolver.resolveStringValue(strVal);
}
};
super.doProcessProperties(beanFactoryToProcess, customValueResolver);
}
}
plugging it into the app
#Configuration
public class PlaceHolderResolverConfig
{
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
PropertySourcesPlaceholderConfigurer placeHolderConfigurer = new CustomPropertySourcesPlaceholderConfigurer();
placeHolderConfigurer.setLocation(new ClassPathResource("application.properties"));
return placeHolderConfigurer;
}
}
In the above example, for all properties starting with something.* nested placeholders wont be resolved..
remove the if(strVal.startsWith("${something.")) check if you want the behaviour for all properties
Related
I'm setting up a Spring Boot (v1.2.6) web project and using Spring Security (v3.2.8). I've found the #PreAuthorize annotation so handy, but I don't know if there's a way to read Boot's properties from the SpEL in the annotation. I'm trying to do it this way:
#PreAuthorize("mysecurity.permit")
With the property declared in application.yml:
mysecurity:
permit: true
But I'm getting
Failed to evaluate expression 'mysecurity.permit'
I've made an attempt with #mysecurity.permit and ${mysecurity.permit} too, with the same result. It seems possible to declare a method in a service and access it in #service.isMySecurityPermited() way, however I would be pleased to know if I'm able to access the property directly.
The values used in an annotation must be constants. They are evaluated at compile time, and while they may be retained for use at runtime they aren't re-evaluated. So you can use an expression that's evaluated by SpEL, or you can write a helper method that is referenced within the annotation value.
If you look at the OnExpressionCondition implementation, you will notice that it gets the value passed to the annotation, which in the case linked in your comment would be something like #ConditionalOnExpression("${server.host==localhost} or ${server.port==8080} ") The annotation simply gets the text value, it has no idea what the text represents, it just knows it's a string. It's in the processing of the annotation value within OnExpressionCondition that the String value takes meaning. They take the String value and pass it to a BeanExpressionResolver for resolution.
So, in your PreAuthorize solution, which based on http://forum.spring.io/forum/spring-projects/security/100708-spel-and-spring-security-3-accessing-bean-reference-in-preauthorize also passes it to an expression processor, you should be able to use spring's expression language to reference any bean property.
I'm not in a situation to test it currently, but from that thread it seems like you could do something like
#Component
public class MyBean {
#Value("${mysecurity.permit}")
private Boolean permit;
public boolean isPermitted() { return permit; }
#PreAuthorize( "#myBean.isPermitted()" )
public blah myMethod() {
// do stuff
}
}
This maybe a generic way to evaluate expressions which i want to share with you:
#Component("AuthorizationComponent")
public final class AuthorizationComponent {
private final static Logger logger = Logger.getLogger(AuthenticationUtils.class.getName());
private static SpelExpressionParser parser;
static {
parser = new SpelExpressionParser();
}
#Autowired
private Environment environment;
public boolean evaluateExpression(final String propertyKey) {
return checkExpression(environment.getProperty(propertyKey));
}
public static boolean checkExpression(String securityExpression) {
logger.info("Checking security expression [" + securityExpression + "]...");
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
Expression exp = parser.parseExpression(securityExpression);
SecurityExpressionRoot context = new CustomMethodSecurityExpressionRoot(authentication);
boolean result = exp.getValue(context, Boolean.class);
logger.info("Check result: " + result);
return result;
}
}
And in yaml config file you can configure the path and authorization expression, something like that:
preAuthorize:
whatever:
post: hasRole('MY_ROLE') OR hasAuthority('MY_AUTHORITY')
Then you could use it like that over your method:
#PreAuthorize("#AuthorizationComponent.evaluateExpression('preAuthorize.whatevert.post')")
#RequestMapping(value = "", method = RequestMethod.POST)
public ResponseEntity<Void> addQuestion(#Valid #RequestBody BodyRestDTO bodyRestDTO){
//Code implementation
return new ResponseEntity<Void>(HttpStatus.CREATED);
}
This should work:
#Value("${mysecurity.permit}")
private Boolean permit;
Then use:
#PreAuthorize(permit)
But you need to properly set configuration file, to allow Spring access it. Read here:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
UPDATE:
Did you configure bean for a property placeholder?
For example:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:com/foo/app.properties</value>
</list>
</property>
</bean>
I'm tying to do a very minimal programmatic/annotation based configuration of Spring, to do some command line stuff and I want to be able to inject value of some bean values from System properties.
I'm using the #Value like this:
#Value("${MigrateDb.task:default}")
private String task;
It's sort of working, but it's not evaluating the value definition, I'm just getting "${MigrateDb.task:default}" in the actual field, instead of Spring evaluating it and giving me the value of the Migrate.db.task system property (or default).
What do I need to add to my Configuration class to enable this behaviour?
try using it this way:
#Value("${MigrateDb.task:default}")
private String task;
XML Config:
<context:property-placeholder
location="your.filelocation.properties" />`
Java Config :
#Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
propertyPlaceholderConfigurer.setLocation(new ClassPathResource("file.properties"));
return propertyPlaceholderConfigurer;
}
From ShadowRay's answer, the minimum code to enable the requested behaviour is:
#Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer(){
return new PropertyPlaceholderConfigurer();
}
Method should be static as per: https://stackoverflow.com/a/14943106/924597
Is there any way for a Spring Boot web application to abort at startup if a required property is not set anywhere (neither in the application.properties file nor the other property sources)? Right now, if the property is included in another property, it seem that Spring Boot simply avoids substitution.
For example, in my application.properties file, I have the line:
quartz.datasource.url=jdbc:hsqldb:${my.home}/database/my-jobstore
Right now, if "my.home" is not set elsewhere, Spring Boot is setting the url literally to "jdbc:hsqldb:${my.home}/database/my-jobstore" (no substitution).
I would like to have the application fail to start if the property my.home were not set anywhere else.
To throw a friendly exceptions just put a default null value in property, check and throw a exception in afterProperty method.
#Component
public static class ConfigurationGuard implements InitializingBean {
#Value("${my.home:#{null}}")
private String myHomeValue;
public void afterPropertiesSet() {
if (this.myHomeValue == null or this.myHomeValue.equals("${my.home}") {
throw new IllegalArgumentException("${my.home} must be configured");
}
}
}
Create a bean with a simple #Value(${my.home}) annotated field. - Then Spring will try to inject that value and will fail and therefore stop when the value is not there.
Just #Value(${my.home}) private String myHomeValue; is enough for normal (not Boot) Spring applications for sure! But I do not know whether Boot has some other configuration to handle missing values: If there is an other failure management than you could check that value in an PostCreation method.
#Component
public static class ConfigurationGuard implements InitializingBean {
#Value(${my.home})
private String myHomeValue;
/**
* ONLY needed if there is some crude default handling for missing values!!!!
*
* So try it first without this method (and without implements InitializingBean)
*/
public void afterPropertiesSet() {
if (this.myHomeValue == null or this.myHomeValue.equals("${my.home}") {
throw new IllegalArgumentException("${my.home} must be configured");
}
}
}
The default behaviour in current versions of Spring Boot (1.5.x, 2.0.x, 2.1.x) is to throw an exception if a placeholder can not be resolved.
There will a be an exception like this one :
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'app.foo.undefined' in value "${app.foo.undefined}"
It works because a bean of type PropertySourcesPlaceholderConfigurer (from spring-context) is automatically registered in Spring Boot, in this class : PropertyPlaceholderAutoConfiguration. And by default, the property ignoreUnresolvablePlaceholders in PropertySourcesPlaceholderConfigurer is set to false, which means an exception must be thrown if a placeholder is unresolved (be it nested or not).
Although they work, I think the approach in the foremost answer is somewhat brittle, as it only works for the predefined name(s), and will silently stop checking the when someone changes quartz.datasource.url in the configs to use a different expansion.
Ideally, I want this value of ignoreUnresolvablePlaceholders to be false to get wholesale expansion checking when parsing my configs such as application.properties or its YAML variants, but it's hard-coded to true for these cases. This unfortunately leaves strings such as ${FOO} in its unexpanded form if FOO cannot be found, making troubleshooting extremely painful. This is especially the case for fields that don't readily appear in the logs such as passwords.
While I couldn't find a way of changing ignoreUnresolvablePlaceholders short of modifying Spring Boot's classes, I did find an alternative of using a custom PropertySource implementation and defining a new syntax such as "${!FOO}" to indicate FOO must exist as an environment variable or die. (The OP didn't mention whether my.home is an environment variable but the code below is for environment variables.)
First, an EnvironmentPostProcessor implementation is required for registering the custom PropertySource. This StrictSystemEnvironmentProcessor.java does this as well as holds the implementation of the custom PropertySource:
package some.package;
#Order(Ordered.LOWEST_PRECEDENCE)
class StrictSystemEnvironmentProcessor implements EnvironmentPostProcessor {
private static final String PROPERTY_SOURCE_NAME = "STRICT_" + StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
if (environment.getPropertySources().contains(PROPERTY_SOURCE_NAME)) {
return;
}
SystemEnvironmentPropertySource delegate = (SystemEnvironmentPropertySource)environment.getPropertySources()
.get(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);
environment.getPropertySources().addLast(new StrictSystemEnvironmentPropertySource(delegate));
}
private static class StrictSystemEnvironmentPropertySource extends SystemEnvironmentPropertySource {
public StrictSystemEnvironmentPropertySource(SystemEnvironmentPropertySource delegate) {
super(PROPERTY_SOURCE_NAME, delegate.getSource());
}
#Override
public Object getProperty(String name) {
if (name.startsWith("!")) {
String variableName = name.substring(1);
Object property = super.getProperty(variableName);
if (property != null) {
return property;
}
throw new IllegalStateException("Environment variable '" + variableName + "' is not set");
}
return null;
}
}
}
Instead of returning null, an exception is thrown for names that start with !.
This META-INF/spring.factories is also required so that Spring initializes our EnvironmentPostProcessor:
org.springframework.boot.env.EnvironmentPostProcessor=some.package.StrictSystemEnvironmentProcessor
Then henceforth, I can write all environment variables substitutions in my configs as ${!FOO} to get strict existance checking.
You can also create a #ConfigurationProperties bean, and decorate it with #Validated and #NotNull. This will throw an exception during startup when the value is not present (or null), e.g.
#Validated
#ConfigurationProperties("my")
public class MyProperties {
#NotNull
private String home;
// getter/setter, or constructor. See #ConstructorBinding.
}
For reference: Spring Boot 2.6 - #ConfigurationProperties Validation.
Note that you may need to add spring-boot-starter-validation, or another validator, depending on your project.
Then, you can just supply it as a dependency when needed, e.g.
#Component
public class AnotherBean {
private final MyProperties myProps;
public AnotherBean(MyProperties myProps) {
this.myProps = myProps;
}
// some code that uses myProps.getHome()
}
I am new for spring security. I've seen many posts on how to inject values via annotation from external property file. I've tried many ways, but I always end up with java.lang.IllegalArgumentException: Could not resolve placeholder 'val.id' exception.
Can you provide me some tips how to handle this exception please?
My java class is the following one:
#Controller
public class Employee {
#Value("${val.id}")
public String valId;
public String getValId() {
return valId;
}
public void setValId(String valId) {
this.valId = valId;
}
My property file is called val.properties which is located under WEB-INF, and its content is
val.id=xyz
I put the following in my main context bean.
<context:property-placeholder location="/WEB-INF/*.properties" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/val.properties"/>
A continuous question:
The injecting values from properties file to annotated beans works fine as I accepted the answer above. However, I cannot able to inject it to #PreAuthorize(...) annotation by following the same procedure.
Assume I want to secure a method called 'update'. This method is allowed if and only if valId is equal to empId. values of valId and empId are initialized in the val.properties file.
my java bean is:
public class Employee {
public String valId;
public String empId;
public String getValId() {
return valId;
}
public void setValId(String valId) {
this.valId = valId;
}
public String getEmpId() {
return empId;
}
public void setEmpId(String empId) {
this.empId = empId;
}
}
my property file contains:
val.id=nn
emp.id=nn
I have the place holder configuration in my main context file:
<context:property-placeholder location="/WEB-INF/*.properties" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertiesFactoryBean"
p:location="/WEB-INF/val.properties"/>
My PreAuthorize annotation (method security) is:
#PreAuthorize("(#{valProp['val.id']} == #{valProp['emp.id']})")
public boolean update(){
//if accessable
return true;
}
But the expression #{valProp['val.id']} == #{valProp['emp.id']} is not evaluated.
Did I do any mistake to inject values? It was worked when I annotate member variables, but it doesn't work here. Any idea please? Thanks in advance.
try to consider the following
1). change your annotation to:
#Value("#{valProp['val.id']}")
2). Replace PropertyPlaceholderConfigurer by PropertiesFactoryBean.
Hope this will resolve the exception.
The reason why the exception is thrown is, because the property placeholder by default throws an exception when a values cannot be resolved.
Furthermore you have two property placeholders, via which probably not all values can be resolved.
You can change this behaviour via setting the ignore-unresolvable property:
<context:property-placeholder location="/WEB-INF/*.properties" ignore-unresolvable="true" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/val.properties" p:ignoreUnresolvablePlaceholders="true" />
Note however that b< turning off this feature typos in a property file will not be detected.
I am trying to access multiple resource bundles from a JSF page. I have two resource bundles:
general_messages.properties
module_message.properties
I want to access both these resource bundles in a JSF file. One way I can do this is to define specific properties for each of these bundles:
<f:loadBundle basename="com.sample.general_messages" var="general"/>
<f:loadBundle basename="com.sample.module_message" var="module"/>
Is there a way I can access both these resource bundles using the same variable name.
Something like:
<f:loadBundle basename="com.sample.general_messages, com.sample.module_message" var="general"/>
Or any other best way to access multiple resource bundles?
You tagged your question with Spring, so I recommend you using Spring MessageSource. Spring MessageSource can aggregate many property files even hierarchically. It gives you many advantages over old java ResourceBundle.
You can define spring MessageSource in you spring-config.xml like this:
<!--
Application messages configuration.
-->
<bean id="messageSource" name="resourceBundle"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
p:fallbackToSystemLocale="false"
p:cacheSeconds="0">
<property name="basenames">
<list>
<value>/messages/Messages</value>
<!-- <value>${application.messages}</value>-->
</list>
</property>
</bean>
Than you can define your Class which extends ResourceBundle like this (Needs some cleaning and refactoring):
public class SpringResourceBundle extends ResourceBundle
{
private MessageSource messages;
private FacesContext fc;
private Locale locale = null;
public SpringResourceBundle()
{
fc = FacesContext.getCurrentInstance();
WebApplicationContext webAppCtx = (WebApplicationContext) fc.getExternalContext().getApplicationMap().get(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
messages = (MessageSource) webAppCtx.getBean("messageSource");
}
#Override
public Locale getLocale()
{
Locale loc = fc.getELContext().getLocale();
if (fc.getExternalContext() != null) {
loc = fc.getExternalContext().getRequestLocale();
}
try {
UIViewRoot viewRoot = fc.getViewRoot();
if (viewRoot != null) {
loc = viewRoot.getLocale();
}
if (loc == null) {
loc = fc.getApplication().getDefaultLocale();
}
} catch (Throwable th) {
System.out.println(th.getMessage());
loc = locale;
}
locale = loc;
return loc;
}
#Override
protected Object handleGetObject(String key)
{
try {
return messages.getMessage(key, null, getLocale());
} catch (NoSuchMessageException e) {
return "???" + key + "???";
}
}
#Override
public Enumeration<String> getKeys()
{
return Collections.enumeration(Collections.EMPTY_LIST);
}
}
Finnaly in faces-config.xml declare your resource bundle with Class above. Something like this:
<application>
<locale-config>
<default-locale>en</default-locale>
<supported-locale>cs</supported-locale>
<supported-locale>de</supported-locale>
<supported-locale>en</supported-locale>
</locale-config>
<message-bundle>your.package.SpringResourceBundle</message-bundle>
</application>
Here you go Spring MessageSource in JSF. Hope it's understandable.
If two resource bundles contain the same key , then which resource bundles should be used to resolve this key ? So ,IMO ,I don't think the same variable name can be assigned to multiple resource bundles.
Perhaps , you can merge all the .properties into a single .properties in your build process (make sure all keys in the merged properties file is unique , for example , by adding some prefix in each key.) . Then you use this single merged .properties throughout the application.
The only situation (that I know of) in which JSF checks multiple files for the same bundle is if you are providing bundles for multiple locales (see Providing Localized Messages and Labels).
You might be able to point the f:loadBundle tag to a class that extends ResourceBundle instead of a properties file and use that class to reference multiple properties files. I haven't tried that before though.
Also, if you are using Seam, it provides the ability to register multiple 'global' bundles as well as bundles that can be associated with one of more views (facelets), all of which can be referenced using messages e.g. #{messages.my_message} as described here (that's for Seam 2, it may be a little different in Seam 3). I think that's what you're after though.