blob: 3fe947e943b799891c626d4ed1c63342526b2437 [file]
[[expressions]]
=== Using Expressions to Create Descriptive, Fine-Grained Rules
Spring Security uses the {spring_framework_reference}expressions.html[Spring Expression Language (SpEL)], which allows you to declare the rules for guarding URLs more descriptively than does the traditional approach, and also allows much more fine-grained rules. Where you traditionally would specify a list of role names and/or special tokens (for example, `IS_AUTHENTICATED_FULLY`), with https://{htmlsingle}#el-access[Spring Security's expression support], you can instead use the embedded scripting language to define simple or complex access rules.
You can use expressions with any of the previously described approaches to securing application URLs. For example, consider this annotated controller:
[source,groovy]
.Listing {counter:listing}. An annotated controller
----
package com.yourcompany.yourapp
import grails.plugin.springsecurity.annotation.Secured
class SecureController {
@Secured("hasRole('ROLE_ADMIN')")
def someAction() {
...
}
@Secured("authentication.name == 'ralph'")
def someOtherAction() {
...
}
}
----
In this example, `someAction` requires `ROLE_ADMIN`, and `someOtherAction` requires that the user be logged in with username "`ralph`".
The corresponding `Requestmap` URLs would be
[source,groovy]
.Listing {counter:listing}. Creating Requestmap instances
----
new Requestmap(url: "/secure/someAction",
configAttribute: "hasRole('ROLE_ADMIN')").save()
new Requestmap(url: "/secure/someOtherAction",
configAttribute: "authentication.name == 'ralph'").save()
----
and the corresponding static mappings would be
[source,groovy]
.Listing {counter:listing}. Adding mappings in `grails.plugin.springsecurity.interceptUrlMap`
----
grails.plugin.springsecurity.interceptUrlMap = [
[pattern: '/secure/someAction', access: ["hasRole('ROLE_ADMIN')"]],
[pattern: '/secure/someOtherAction', access: ["authentication.name == 'ralph'"]]
]
----
The Spring Security docs have a https://{htmlsingle}#el-common-built-in[table listing the standard expressions], which is copied here for reference:
.Spring Security expressions
[cols="50,50"]
|====================
| *Expression* | *Description*
|`hasRole(role)`
|Returns `true` if the current principal has the specified role
|`hasAnyRole([role1,role2])`
|Returns `true` if the current principal has any of the supplied roles (given as a comma-separated list of strings)
|`principal`
|Allows direct access to the principal object representing the current user
|`authentication`
|Allows direct access to the current `Authentication` object obtained from the `SecurityContext`
|`permitAll`
|Always evaluates to `true`
|`denyAll`
|Always evaluates to `false`
|`isAnonymous()`
|Returns `true` if the current principal is an anonymous user
|`isRememberMe()`
|Returns `true` if the current principal is a remember-me user
|`isAuthenticated()`
|Returns `true` if the user is not anonymous
|`isFullyAuthenticated()`
|Returns `true` if the user is not an anonymous or a remember-me user
|`request`
|the HTTP request, allowing expressions such as "`isFullyAuthenticated() or request.getMethod().equals('OPTIONS')`"
|====================
In addition, you can use a web-specific expression `hasIpAddress`. However, you may find it more convenient to separate IP restrictions from role restrictions by using the IP address filter (<<ip>>).
To help you migrate traditional configurations to expressions, this table compares various configurations and their corresponding expressions:
.Traditional configurations and associated expressions
[cols="50,50"]
|====================
| *Traditional Config* | *Expression*
|`ROLE_ADMIN`
|`hasRole('ROLE_ADMIN')`
|`ROLE_USER,ROLE_ADMIN`
|`hasAnyRole('ROLE_USER','ROLE_ADMIN')`
|`ROLE_ADMIN,IS_AUTHENTICATED_FULLY`
|`hasRole('ROLE_ADMIN') and isFullyAuthenticated()`
|`IS_AUTHENTICATED_ANONYMOUSLY`
|`permitAll`
|`IS_AUTHENTICATED_REMEMBERED`
|`isAuthenticated() or isRememberMe()`
|`IS_AUTHENTICATED_FULLY`
|`isFullyAuthenticated()`
|====================