3

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.

Aritz
  • 30,971
  • 16
  • 136
  • 217

3 Answers3

5

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
  }
}
digitaljoel
  • 26,265
  • 15
  • 89
  • 115
  • Makes sense, but Spring enables using annotations based in properties. It might be achieved using proxies or similars. Check out this links. http://stackoverflow.com/questions/26451321/spring-boot-conditionalonproperty-or-conditionalonexpression http://stackoverflow.com/questions/26394778/what-is-purpose-of-conditionalonproperty-annotation I'm just bothering if that's possible for the `@PreAuthorized` annotation. – Aritz Jan 11 '16 at 20:47
  • I wanted to avoid setting the value in a bean variable, but will give it a try all in all. +1 for your research efforts ;-) – Aritz Jan 11 '16 at 21:18
  • 1
    Guys, we are now almost in 2021, is there any possiblity to use ${mysecurity.permit} directly in the PreAuthorize annotation ? Thanks for any updated answer. – Ashbay Nov 12 '20 at 21:45
1

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);
}
mibrahim.iti
  • 1,928
  • 5
  • 22
  • 50
0

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>
m.aibin
  • 3,528
  • 4
  • 28
  • 47
  • The property is being recognized in other classes (as `@ConditionalOnProperty(value = ${mysecurity.permit}")`), so the property itself is not a problem. The issue seems to be accessing it from the `@PreAuthorize`. – Aritz Jan 11 '16 at 17:22