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>
Related
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
I read a post about authentication with facebook, http://www.jasha.eu/blogposts/2013/09/retrieve-facebook-profile-data-java-spring-social.html
he use autowired to inject 3 arguments to the constructor. which gives me SpelEvaluationException.
My project, I add a config.properties under /src/
facebook.clientId=237473571343
facebook.clientSecret=9iuwijig[sa[w90u2tjgjgj
application.host=http://localhost:8080
and the constructor
#Controller
public class FacebookSpringSocialAuthenticator {
public static final String STATE = "state";
private String applicationHost;
private FacebookConnectionFactory facebookConnectionFactory;
#Autowired
public FacebookSpringSocialAuthenticator(
#Value("#{properties['facebook.clientId']}")
String clientId,
#Value("#{properties['facebook.clientSecret']}")
String clientSecret,
#Value("#{properties['application.host']}")
String applicationHost) {
this.applicationHost = applicationHost;
facebookConnectionFactory =
new FacebookConnectionFactory(clientId, clientSecret);
}
}
after search and reading discussions and docs, I still don't figure out what to do.
the #{} is spring EL support, don't know how to use it, and don't know the difference to ${}
if I change to #Value("${facebook.clientId}"), there will be no exception, then I use debug mode to read the value of clientId, it does not show 237473571343, it shows ${facebook.clientId}, is that working correctly?
#Value("#{properties['facebook.clientId']}")
In your #Value annotation you are using a SpEL expression. In your case it is going to look for a Map or Properties object named properties and try to find a property with the key facebook.clientId.
To make it work you need to add the following
<util:properties id="properties" location="config.properties" />
Although this works I would strongly suggest to use a *PlaceHolderConfigurer instead of using SpEL.First add atag to your configuration, next change your#Value` to simply use properties.
<context:property-placeholder location="config.properties" />
Then your #Value can be like
#Value("${facebook.clientId}")
The added advantage of this is that you could also use system or environment properties to do some configuration (or override parts of your configuration).
Is there a way to create more expressive statements in #Preauthorize blocks? Here's an example of something I find myself repeating, because the #Preauthorize is not terribly smart out of the box.
#RequestMapping(value = "{id}", method = RequestMethod.DELETE)
public void deleteGame(#PathVariable int id, #ModelAttribute User authenticatingUser) {
Game currentGame = gameService.findById(id);
if(authenticatingUser.isAdmin() || currentGame.getOwner().equals(authenticatingUser)) {
gameService.delete(gameService.findById(id));
} else {
throw new SecurityException("Only an admin, or an owner can delete a game.");
}
}
What I would prefer is something like.
#RequestMapping(value = "{id}", method = RequestMethod.DELETE)
#Preauthorize(isAdmin(authenicatingUser) OR isOwner(authenicatingUser, id)
public void deleteGame(#PathVariable int id, #ModelAttribute User authenticatingUser, #ModelAttribute currentGame ) { //I'm not sure how to add this either :(
gameService.delete(gameService.findById(id));
}
Part of the problem is that I need to make a query to the database to fetch some of this stuff to verify permissions, such as querying the database to get a copy of the game, and then comparing the owner of the game to the person making the request. I'm not really sure how all of that operates within the context of a #Preauthorize annotation processor, or how I add things to the collection of objects made available in the #Preauthorize("") value attribute.
Since #PreAuthorize evaluates SpEl-expressions, the easiest way is just to point to a bean:
#PreAuthorize("#mySecurityService.someFunction()")
MySecurityService.someFunction should have return type boolean.
Spring-security will automatically provide a variable named authentication if you want to pass the Authentication-object. You can also use any valid SpEl-expressions to access any arguments passed to your secure method, evaluate regular expressions, call static methods, etc. E.g:
#PreAuthorize("#mySecurityService.someFunction(authentication, #someParam)")
1) First you have to reimplement MethodSecurityExpressionRoot which contains extra method-specific functionality. The original Spring Security implementation is package private and hence it is not possible to just extend it. I suggest checking the source code for the given class.
public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
// copy everything from the original Spring Security MethodSecurityExpressionRoot
// add your custom methods
public boolean isAdmin() {
// do whatever you need to do, e.g. delegate to other components
// hint: you can here directly access Authentication object
// via inherited authentication field
}
public boolean isOwner(Long id) {
// do whatever you need to do, e.g. delegate to other components
}
}
2) Next you have to implement custom MethodSecurityExpressionHandler that will use the above defined CustomMethodSecurityExpressionRoot.
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
#Override
public void setReturnObject(Object returnObject, EvaluationContext ctx) {
((MethodSecurityExpressionRoot) ctx.getRootObject().getValue()).setReturnObject(returnObject);
}
#Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
MethodInvocation invocation) {
final CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
3) Define expression handler bean in your context, e.g. via XML you can do it as follows
<bean id="methodSecurityExpressionHandler"
class="my.package.CustomMethodSecurityExpressionHandler">
<property name="roleHierarchy" ref="roleHierarchy" />
<property name="permissionEvaluator" ref="permissionEvaluator" />
</bean>
4) Register the above defined handler
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="methodSecurityExpressionHandler"/>
</security:global-method-security>
5) Then just use the defined expressions in your #PreAuthorize and/or #PostAuthorize annotations
#PreAuthorize("isAdmin() or isOwner(#id)")
public void deleteGame(#PathVariable int id, #ModelAttribute currentGame) {
// do whatever needed
}
And one more thing. It is not very common to use method level security to secure controller methods but rather to secure methods with business logic (a.k.a. your service layer methods). Then you could use something like the below.
public interface GameService {
// rest omitted
#PreAuthorize("principal.admin or #game.owner = principal.username")
public void delete(#P("game") Game game);
}
But keep in mind that this is just an example. It expects that the actual principal has isAdmin() method and that the game has getOwner() method returning username of the owner.
You could write your annotation something like:
#PreAuthorize("hasRole('ROLE_ADMIN') and hasPermission(#id, 'Game', 'DELETE')")
To get the hasPermission part working you need to implement PermissionEvaluator interface.
Then define an expression handler bean:
#Autowired
private PermissionEvaluator permissionEvaluator;
#Bean
public DefaultMethodSecurityExpressionHandler expressionHandler()
{
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(permissionEvaluator);
return handler;
}
And inject in your security config:
<global-method-security pre-post-annotations="enabled">
<expression-handler ref="expressionHandler" />
</global-method-security>
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.
(Sorry if this is a duplicate, but the question is very search-engine-unfriendly.)
I want to know how to evaluate Spring EL inside EL (with all the functions, variables, context, etc. passed through).
Specifically, I want to dynamically evaluate a Spring Security expression (which is just EL plus some functions and contexts) loaded from a database entity inside a hard-coded EL in #PreAuthorize.
I.e. something like #PreAuthorize("eval(argument.securityExpr)").
You can extend Springs MethodSecurityExpressionRoot (and create it in your own MethodSecurityExpressionHandler) and add an eval method which excepts a String and let the SpringExpressionParser evaluate the String. Should work...
Edit:
A little code:
public class MySpringSecurityRoot extends MethodSecurityExpressionRoot {
private MyMethodSecurityExpressionHandler handler; // to be injected in the handler
public boolean eval(String expression) {
Expression expression = handler.getExpressionParser().parseExpression(expression);
return ExpressionUtils.evaluateAsBoolean(
handler.getExpressionParser().parseExpression(expression),
handler.createEvaluationContext(authentification, methodInvocation));
}
}
your handler must be set as the default method security expression handler:
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="myHandler"/>
</security:global-method-security>
now your eval function is accessible in every method security expression
BUT: You must be aware of the fact that the person who describes your security rule, can access all beans inside the current spring context! Might be a security leak.
If you use a simpleparametername discover with # to evaluate permissions you can do virtually anything you want, without enabling debug mode.
#PreAuthorize("#mySecurityService.hasPermission(#arg0)")
public String getSpecial(final String special) {
return "authorized";
}
mySecurityService can be any bean/method returning a boolean, with this wired up for arg1
public class SimpleParameterNameDiscoverer implements ParameterNameDiscoverer {
public String[] getParameterNames(Method m) {
return getParameterNames(m.getParameterTypes().length);
}
public String[] getParameterNames(Constructor c) {
return getParameterNames(c.getParameterTypes().length);
}
protected String[] getParameterNames(int length) {
String[] names = new String[length];
for (int i = 0; i < length; i++)
names[i] = "arg" + i;
return names;
}
}
and context :
<bean id="methodSecurityExpressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="parameterNameDiscoverer">
<bean class="your.path.SimpleParameterNameDiscoverer"/>
</property>