Spring Security 3.0 introduced the ability to use Spring EL expressions as an authorization mechanism in addition to the simple use of configuration attributes and access-decision voters which have been seen before.
Expression-based access control is built on the same architecture but allows complicated Boolean logic to be encapsulated in a single expression.
=== Overview
Spring Security uses Spring EL for expression support and you should look at how that works if you are interested in understanding the topic in more depth.
Expressions are evaluated with a "root object" as part of the evaluation context.
Spring Security uses specific classes for web and method security as the root object, in order to provide built-in expressions and access to values such as the current principal.
[[el-common-built-in]]
==== Common Built-In Expressions
The base class for expression root objects is `SecurityExpressionRoot`.
This provides some common expressions which are available in both web and method security.
| Returns `true` if the user has access to the provided target for the given permission.
For example, `hasPermission(1, 'com.example.domain.Message', 'read')`
|===
[[el-access-web]]
=== Web Security Expressions
To use expressions to secure individual URLs, you would first need to set the `use-expressions` attribute in the `<http>` element to `true`.
Spring Security will then expect the `access` attributes of the `<intercept-url>` elements to contain Spring EL expressions.
The expressions should evaluate to a Boolean, defining whether access should be allowed or not.
For example:
[source,xml]
----
<http>
<intercept-url pattern="/admin*"
access="hasRole('admin') and hasIpAddress('192.168.1.0/24')"/>
...
</http>
----
Here we have defined that the "admin" area of an application (defined by the URL pattern) should only be available to users who have the granted authority "admin" and whose IP address matches a local subnet.
We've already seen the built-in `hasRole` expression in the previous section.
The expression `hasIpAddress` is an additional built-in expression which is specific to web security.
It is defined by the `WebSecurityExpressionRoot` class, an instance of which is used as the expression root object when evaluating web-access expressions.
For example, if the URL were `/user/123/resource`, then the id passed in would be `123`.
=== Method Security Expressions
Method security is a bit more complicated than a simple allow or deny rule.
Spring Security 3.0 introduced some new annotations in order to allow comprehensive support for the use of expressions.
[[el-pre-post-annotations]]
==== @Pre and @Post Annotations
There are four annotations which support expression attributes to allow pre and post-invocation authorization checks and also to support filtering of submitted collection arguments or return values.
They are `@PreAuthorize`, `@PreFilter`, `@PostAuthorize` and `@PostFilter`.
Their use is enabled through the `global-method-security` namespace element:
Here we're actually using a method argument as part of the expression to decide whether the current user has the "admin" permission for the given contact.
The built-in `hasPermission()` expression is linked into the Spring Security ACL module through the application context, as we'll <<el-permission-evaluator,see below>>.
Behind the scenes this is implemented using `AnnotationParameterNameDiscoverer` which can be customized to support the value attribute of any specified annotation.
Behind the scenes this is implemented using `AnnotationParameterNameDiscoverer` which can be customized to support the value attribute of any specified annotation.
* If JDK 8 was used to compile the source with the -parameters argument and Spring 4+ is being used, then the standard JDK reflection API is used to discover the parameter names.
This works on both classes and interfaces.
* Last, if the code was compiled with the debug symbols, the parameter names will be discovered using the debug symbols.
This will not work for interfaces since they do not have debug information about the parameter names.
For interfaces, annotations or the JDK 8 approach must be used.
.[[el-pre-post-annotations-spel]]
--
Any Spring-EL functionality is available within the expression, so you can also access properties on the arguments.
For example, if you wanted a particular method to only allow access to a user whose username matched that of the contact, you could write
When using the `@PostFilter` annotation, Spring Security iterates through the returned collection or map and removes any elements for which the supplied expression is false.
For an array, a new array instance will be returned containing filtered elements.
In case when a map is used it will refer to the current `Map.Entry` object which allows one to use `filterObject.key` or `filterObject.value` in the expresion.
You can also filter before the method call, using `@PreFilter`, though this is a less common requirement.
The syntax is just the same, but if there is more than one argument which is a collection type then you have to select one by name using the `filterTarget` property of this annotation.
Note that filtering is obviously not a substitute for tuning your data retrieval queries.
If you are filtering large collections and removing many of the entries then this is likely to be inefficient.
[[el-method-built-in]]
==== Built-In Expressions
There are some built-in expressions which are specific to method security, which we have already seen in use above.
The `filterTarget` and `returnValue` values are simple enough, but the use of the `hasPermission()` expression warrants a closer look.
[[el-permission-evaluator]]
===== The PermissionEvaluator interface
`hasPermission()` expressions are delegated to an instance of `PermissionEvaluator`.
It is intended to bridge between the expression system and Spring Security's ACL system, allowing you to specify authorization constraints on domain objects, based on abstract permissions.
It has no explicit dependencies on the ACL module, so you could swap that out for an alternative implementation if required.
which map directly to the available versions of the expression, with the exception that the first argument (the `Authentication` object) is not supplied.
The first is used in situations where the domain object, to which access is being controlled, is already loaded.
Then expression will return true if the current user has the given permission for that object.
The second version is used in cases where the object is not loaded, but its identifier is known.
An abstract "type" specifier for the domain object is also required, allowing the correct ACL permissions to be loaded.
This has traditionally been the Java class of the object, but does not have to be as long as it is consistent with how the permissions are loaded.
To use `hasPermission()` expressions, you have to explicitly configure a `PermissionEvaluator` in your application context.