Groovy

Grails: Calling bean methods in Spring Security expressions

Some days ago while working on a Grails application I was in a situation where a wanted to call a bean method from a Spring security SPEL expression. I was using the @PreAuthorize annotation from the Spring Security ACL plugin and wanted to do something like this:
 
 
 
 
 
 
 

@PreAuthorize("myService.canAccessUserProfile(#profileId)")
public Profile getUserProfile(long profileId) {
  ...
}

@PreAuthorize takes a SPEL expression as parameter that is evaluated to see if the currently logged in user is allowed to access the getUserProfile() method. Within this SPEL expression I wanted to call the canAccessUserProfile() method of myService for performing the security check. In the following I will explain which steps are necessary to make this working.

Luckily it is possible to reference beans from SPEL expressions by prefixing their name with an @ symbol:

@PreAuthorize("@myService.canAccessUserProfile(#profileId)")

However, this change alone won’t work out of the box and we have to do some small adjustments on the Spring Security configuration.

The SPEL expression parser (see: SpelExpressionParser) that is used to parse the security expression delegates the lookup of beans to a BeanResolver. In order to make the security expression from above work we have to create a BeanResolver implementation and add it to the Spring Security configuration.

Creating a BeanResolver for Grails is very simple:

class GrailsBeanResolver implements BeanResolver {

  GrailsApplication grailsApplication

  @Override
  public Object resolve(EvaluationContext evaluationContext, String beanName) throws AccessException {
    return grailsApplication.mainContext.getBean(beanName)
  }
}

We only have to implement the resolve() method to satisfy the BeanResolver interface. In our example we delegate this job to the bean factory of our Grails application. So, we can access all available beans within security expressions.

Now we have to add our bean resolver to the SPEL evaluation context (see: EvaluationContext). This can be done by overriding the createEvaluationContext() of Spring Securities DefaultMethodSecurityExpressionHandler:

class GrailsExpressionHandler extends DefaultMethodSecurityExpressionHandler {

  BeanResolver beanResolver

  @Override
  public EvaluationContext createEvaluationContext(Authentication auth, MethodInvocation method) {
    StandardEvaluationContext context = (StandardEvaluationContext) super.createEvaluationContext(auth, method)
    context.setBeanResolver(beanResolver)
    return context;
  }
}

As the name suggests createEvaluationContext() is responsible for creating the evaluation context for security expressions. The only thing we do is adding a beanResovler after the EvaluationContext has been created.

After that, we have to configure our two new beans in grails-app/conf/spring/resources.groovy using the Spring Bean DSL:

beans = {
  expressionHandler(GrailsExpressionHandler) {
    beanResolver              = ref('beanResolver')
    parameterNameDiscoverer   = ref('parameterNameDiscoverer')
    permissionEvaluator       = ref('permissionEvaluator')
    roleHierarchy             = ref('roleHierarchy')
    trustResolver             = ref('authenticationTrustResolver')  
  }

  beanResolver(GrailsBeanResolver) {
    grailsApplication = ref('grailsApplication')
  }
}

Normally the expressionHandler bean would be an instance of DefaultMethodSecurityExpressionHandler as mentioned above. Since we want Spring Security to use our GrailsExpressionHandler we have to override the expressionHandler bean. The only new dependency we added is the beanResolver property. The other four dependencies of expressionHandler are required by DefaultMethodSecurityExpressionHandler (the base class of GrailsExpressionHandler). These dependencies are already provided by the Spring Security plugins.

Now it should be possible to reference beans using the @ prefix and call their methods in security expressions.
 

Michael Scharhag

Michael Scharhag is a Java Developer, Blogger and technology enthusiast. Particularly interested in Java related technologies including Java EE, Spring, Groovy and Grails.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button