Evaluation of Spring Security expressions programmatically
Well, it is already some time ago I had written something about using the Spring ecosystem. After this (very) long pause, I will try to catch up. In this post we will have a deeper look at the Spring Expression Language (SpEL) and especially in context of Spring Security and its authentication checks.
The goal of this explanation is the answer to the following question respective scenario: How can we evaluate authorisation checks like defined in
@Secured on demand?
The complete working demo is available at GitHub. PRs are open in case of errors or additions.
Or, in a more specific way questioned: Let’s say we have a controller like this:
This setup would allow requests to
/users/1 only if the user does have the permission
READ for the given user id. This is a no-brainer when using permission checks.
But how can we call this programmatically? Something like
Of course, the functions supported in this SpEL-context are not unique and there are alternatives which do not require following that question at first. A
hasPermission() is implemented by the
PermissionEvaluator which you may have exposed via a bean. The
hasRole() is tricky: You may think ’that is easy’, just use
UserDetails#getAuthorities() and yes, maybe it is enough. However, the standard behavior does not extend the authorities (which includes the roles) when a role hierarchy is defined. The actual interpolation takes place in the evaluation unit therefore the users authorities does not includes inherited roles.
Let’s change the example being a bit more complex:
Technically, this is a complex SpEL (expression). Let’s assume we want to render a link to this endpoint but only if the (current) user fulfills the required permissions (either having the permission
DELETE of the user or having the role
ADMIN). Obviously, we can rebuild the logic of this permission expression based on actual permission evaluator and checks of the actual user and its roles. However, things getting complex if (security) checks are being located multiple times at different places.
It would be far better if we can use the same expression language. And of top of it: exactly the same expression.
Also, you may have the requirement of having configurable expressions. In this case expression strings are a solution.
We have a goal. And as usual, there is a bonus at the end.
At first, we should prepare the basics. Given a standard Spring Boot project (at time of writing 2.6.4) with the starters
spring-boot-starter-security start.spring.io, we have to configure Spring Security enabling method security (for enabling
@PreAuthorize). The compile dependency Lombok is also included for having short examples.
For the sake of simplicity in this prototype/demo, we set the standard user’s password to a static value. Alternatively, pick the generated one of the console. We also include a static role for demo purpose.
This enables the following controller setup will work as expected:
After starting the application, we can verify this:
hasRole() would also work if the (default demo) user has roles configured. In this poc-scenario is one authority
In order of supporting the check of permissions on targets (i.e.
hasPermission(…)), we need a bean of type
PermissionEvaluator. You may skip this chapter if permissions are not in scope.
Technically, if such a bean does not exist, the internal default will rely to a deny-all functionality.
Note: That’s a bad evaluator. Don’t use it in production. Really. :)
Exposed via a bean in the already mentioned
SecurityConfig, the extending
GlobalMethodSecurityConfiguration will pickup this and register it automatically in a method-security-expression-handler. The latter one will be important in the over-next chapter.
Excursion: Spring Expression Language (SpEL)
First of all, some bits about SpEL. Basically, it is a limited language for building and evaluating (small) expressions and is used in several places within the Spring ecosystem: in filters of Spring Integration, in conditions of
@EventListener, or here in authorize checks.
In context of Spring Security, this is basically these lines:
expr and even
result are straight forward and do not require more description. Of course, the expression
str can be a complex one with arguments or conditions. However, building
context is challenging.
At first, we can use the standard context
StandardEvaluationContext and define a root object. The context is the “holder” of all relevant things within the (following) evaluation of the expression:
rootobject which expresses which functions are available (like
- additional resolver for accessing objects, i.e. beans or other objects
- internal objects (we see later, that a more specific context contains the current authentication already)
- additional variables
Evaluator of SpEL for Spring Security
Now, we need the glue code between Spring Security and the SpEL evaluation: the custom
As noted above already, the context requires a
root and an optional
beanResolver for resolving the tokens in the expressions correctly:
isAuthenticated and so on. As we are speaking of expressions labeled on methods, there is the need of supporting the resolve of method’s arguments like
#id also. That’s why internally
MethodSecurityEvaluationContext as well as
MethodSecurityExpressionRoot is being used. This pair interacts via reflection with the method signature and provide deep inspects about the actual method argument values. Thankfully, this is already done when using
Putting things together we get this:
evaluateExpression("isAuthenticated()") creates the parser and evaluates the given expression using the current authentication. The pseudo type and pseudo method is required as we need a pseudo target for the “method invocation”.
Because we are using an expression handler intended for method invocations, we are faking the actual method call (because not having one and
null is not allowed) and the variables must be provided explicitly.
This is a working example of usage:
Finally, we register this evaluator in the already mentioned
SecurityConfig. The default expression handler is provided by the
GlobalMethodSecurityConfiguration parent class.
And this enables us to use the evaluator at any place, here in a controller:
Part one solved: This evaluator enables us to use the same SpEL as in
At this time, we are now able to check security authentication checks wherever we are in the spring context. Unless the authentication is available (like in a service/daemon context), you may creating a complete different context and/or a custom on-demand “pseudo” authentication object.
Re-use existing defined @PreAuthorize definitions on methods
Now, with the created
AppPermissionExpressionEvaluator we still have the problem we have to define the expression multiple times. However, using Java’s reflection api and a tooling function we can archive the goal of having exactly one definition.
Note: The following example takes place only in the
UserController which is not required. The only requirement is the controller’s method is visible of the initiating class.
With the following tooling extension in
we have the right tooling using the existing expression of a controller method.
Now we are complete!
Bonus: Custom authorization functions like hasRole or hasPermission
As already mentioned, the functions
hasPermission() and others are actually placed in the
root object. This also means we can extend this root for additional custom functions.
Let’s say we want to have the ability of writing such an expression:
A highly individual check and explicitly bullshit. But you got it.
We start of creating a class
AppMethodSecurityExpressionRoot which extends the required
SecurityExpressionRoot and provide additional api via implementing
MethodSecurityExpressionOperations (because we are still using this in the
We cannot just extend
MethodSecurityExpressionRoot because it is packaged protected. So we are in need of copying some stuff…
After this, we can define the actual extension functions:
Finally, we need to instruct having a method-security-expression-handler with a custom root.
In order to use the custom root, we have to create custom method-security-expression-handler. Luckily, in this case we can just override the default one.
The configuration class
GlobalMethodSecurityConfiguration which already have the extension point
createExpressionHandler() for overriding with a custom handler. However, as we need this handler also for the bean
AppPermissionExpressionEvaluator, we have to move out the construction. Please also have in mind, that
GlobalMethodSecurityConfiguration is doing some post-init instructions onto the (default) expression handler which we may also want to apply.
The following addition into
SecurityConfig defines a sub configuration which applies the same initialization as the default one. The important thing however is the bean
public MethodSecurityExpressionHandler methodSecurityExpressionHandler() which creates the
Most of the following one is also copied stuff..
Note: IntelliJ IDEA provides a deep inspection into Security-SpEL expression. However, custom functions are not supported yet. Meanwhile, use this workaround for a working validation.
The complete working demo is available at GitHub. PRs are open in case of errors or additions.
For more information about Spring, Spring Boot, Spring Security you should have a look at their documentation, reference guides and tutorials at spring.io.