5 Helper Classes - Reference Documentation
Authors: Burt Beckwith, Beverley Talbott
Version: 2.0.0
5 Helper Classes
Use the plugin helper classes in your application to avoid dealing with some lower-level details of Spring Security.5.1 SecurityTagLib
The plugin includes GSP tags to support conditional display based on whether the user is authenticated, and/or has the required role to perform a particular action. These tags are in thesec
namespace and are implemented in grails.plugin.springsecurity.SecurityTagLib
.ifLoggedIn
Displays the inner body content if the user is authenticated.Example:<sec:ifLoggedIn> Welcome Back! </sec:ifLoggedIn>
ifNotLoggedIn
Displays the inner body content if the user is not authenticated.Example:<sec:ifNotLoggedIn> <g:link controller='login' action='auth'>Login</g:link> </sec:ifNotLoggedIn>
ifAllGranted
Displays the inner body content only if all of the listed roles are granted.Example:<sec:ifAllGranted roles="ROLE_ADMIN,ROLE_SUPERVISOR">secure stuff here</sec:ifAllGranted>
ifAnyGranted
Displays the inner body content if at least one of the listed roles are granted.Example:<sec:ifAnyGranted roles="ROLE_ADMIN,ROLE_SUPERVISOR">secure stuff here</sec:ifAnyGranted>
ifNotGranted
Displays the inner body content if none of the listed roles are granted.Example:<sec:ifNotGranted roles="ROLE_USER">non-user stuff here</sec:ifNotGranted>
loggedInUserInfo
Displays the value of the specified UserDetails field if logged in. For example, to show the username property:<sec:loggedInUserInfo field="username"/>
fullName
property, you access it as follows:Welcome Back <sec:loggedInUserInfo field="fullName"/>
username
Displays the value of the UserDetailsusername
field if logged in.<sec:ifLoggedIn> Welcome Back <sec:username/>! </sec:ifLoggedIn> <sec:ifNotLoggedIn> <g:link controller='login' action='auth'>Login</g:link> </sec:ifNotLoggedIn>
ifSwitched
Displays the inner body content only if the current user switched from another user. (See also Switch User.)<sec:ifLoggedIn> Logged in as <sec:username/> </sec:ifLoggedIn><sec:ifSwitched> <a href='${request.contextPath}/j_spring_security_exit_user'> Resume as <sec:switchedUserOriginalUsername/> </a> </sec:ifSwitched><sec:ifNotSwitched> <sec:ifAllGranted roles='ROLE_SWITCH_USER'> <form action='${request.contextPath}/j_spring_security_switch_user' method='POST'> Switch to user: <input type='text' name='j_username'/><br/> <input type='submit' value='Switch'/> </form> </sec:ifAllGranted></sec:ifNotSwitched>
ifNotSwitched
Displays the inner body content only if the current user has not switched from another user.switchedUserOriginalUsername
Renders the original user's username if the current user switched from another user.<sec:ifSwitched> <a href='${request.contextPath}/j_spring_security_exit_user'> Resume as <sec:switchedUserOriginalUsername/> </a> </sec:ifSwitched>
access
Renders the body if the specified expression evaluates totrue
or specified URL is allowed.<sec:access expression="hasRole('ROLE_USER')">You're a user</sec:access>
<sec:access url="/admin/user"><g:link controller='admin' action='user'>Manage Users</g:link></sec:access>
<sec:access controller='admin' action='user'><g:link controller='admin' action='user'>Manage Users</g:link></sec:access>
<sec:access mapping='manageUsers'><g:link mapping='manageUsers'>Manage Users</g:link></sec:access>
createLink
to build the URL, for example<sec:access url='${createLink(controller: 'admin', action: 'user', base: "/")}'><g:link controller='admin' action='user'>Manage Users</g:link></sec:access>
base: "/"
attribute in this case to avoid appending the context name to the URL.noAccess
Renders the body if the specified expression evaluates tofalse
or URL isn't allowed.<sec:noAccess expression="hasRole('ROLE_USER')">You're not a user</sec:noAccess>
link
A wrapper around the standard Grails link tag that renders if the specified expression evaluates totrue
or URL is allowed.To define the expression to evaluate within the tag itself:<sec:link controller="myController" action="myAction" expression="hasRole('ROLE_USER')">My link text</sec:link>
<sec:link controller="myController" action="myAction">My link text</sec:link>
5.2 SpringSecurityService
grails.plugin.springsecurity.SpringSecurityService
provides security utility functions. It is a regular Grails service, so you use dependency injection to inject it into a controller, service, taglib, and so on:def springSecurityService
getCurrentUser()
Retrieves a domain class instance for the currently authenticated user. During authentication a user/person domain class instance is retrieved to get the user's password, roles, etc. and the id of the instance is saved. This method uses the id and the domain class to re-load the instance, or the username if theUserDetails
instance is not a GrailsUser
.If you do not need domain class data other than the id, you should use the loadCurrentUser
method instead.Example:class SomeController { def springSecurityService def someAction() { def user = springSecurityService.currentUser … } }
loadCurrentUser()
Often it is not necessary to retrieve the entire domain class instance, for example when using it in a query where only the id is needed as a foreign key. This method uses the GORMload
method to create a proxy instance. This will never be null, but can be invalid if the id doesn't correspond to a row in the database, although this is very unlikely in this scenario because the instance would have been there during authentication.If you need other data than just the id, use the getCurrentUser
method instead.Example:class SomeController { def springSecurityService def someAction() { def user = springSecurityService.isLoggedIn() ? springSecurityService.loadCurrentUser() : null if (user) { CreditCard card = CreditCard.findByIdAndUser( params.id as Long, user) … } … } }
isLoggedIn()
Checks whether there is a currently logged-in user.Example:class SomeController { def springSecurityService def someAction() { if (springSecurityService.isLoggedIn()) { … } else { … } } }
getAuthentication()
Retrieves the current user's Authentication. If authenticated, this will typically be a UsernamePasswordAuthenticationToken.If not authenticated and the AnonymousAuthenticationFilter is active (true by default) then the anonymous user's authentication will be returned. This will be an instance ofgrails.plugin.springsecurity.authentication. GrailsAnonymousAuthenticationToken
with a standard org.springframework.security.core.userdetails.User
instance as its Principal. The authentication will have a single granted role, ROLE_ANONYMOUS
.Example:class SomeController { def springSecurityService def someAction() { def auth = springSecurityService.authentication String username = auth.username // a Collection of GrantedAuthority def authorities = auth.authorities boolean authenticated = auth.authenticated … } }
getPrincipal()
Retrieves the currently logged in user'sPrincipal
. If authenticated, the principal will be a grails.plugin.springsecurity.userdetails.GrailsUser
, unless you have created a custom UserDetailsService
, in which case it will be whatever implementation of UserDetails you use there.If not authenticated and the AnonymousAuthenticationFilter is active (true by default) then a standard org.springframework.security.core.userdetails.User
is used.Example:class SomeController { def springSecurityService def someAction() { def principal = springSecurityService.principal String username = principal.username // a Collection of GrantedAuthority def authorities = principal.authorities boolean enabled = principal.enabled … } }
encodePassword()
Hashes a password with the configured hashing scheme. By default the plugin uses bcrypt, but you can configure the scheme with thegrails.plugin.springsecurity.password.algorithm
attribute in Config.groovy
. The supported values are 'bcrypt' to use bcrypt, 'pbkdf2' to use PBKDF2, or any message digest algorithm that is supported in your JDK; see this Java page for the available algorithms.
You are strongly discouraged from using MD5 or SHA-1 algorithms because of their well-known vulnerabilities. You should also use a salt for your passwords, which greatly increases the computational complexity of computing passwords if your database gets compromised. See Salted Passwords.Example:
class PersonController { def springSecurityService def updateAction() { def person = Person.get(params.id) params.salt = person.salt if (person.password != params.password) { params.password = springSecurityService.encodePassword( password, salt) def salt = … // e.g. randomly generated using a utility method params.salt = salt } person.properties = params if (!person.save(flush: true)) { render view: 'edit', model: [person: person] return } redirect action: 'show', id: person.id } }
If you are hashing the password in the User domain class (usingbeforeInsert
andencodePassword
) then don't callspringSecurityService.encodePassword()
in your controller since you'll double-hash the password and users won't be able to log in. It's best to encapsulate the password handling logic in the domain class.
updateRole()
Updates a role and, if you useRequestmap
instances to secure URLs, updates the role name in all affected Requestmap
definitions if the name was changed.Example:class RoleController { def springSecurityService def update() { def roleInstance = Role.get(params.id) if (!springSecurityService.updateRole(roleInstance, params)) { render view: 'edit', model: [roleInstance: roleInstance] return } flash.message = "The role was updated" redirect action: show, id: roleInstance.id } }
deleteRole()
Deletes a role and, if you useRequestmap
instances to secure URLs, removes the role from all affected Requestmap
definitions. If a Requestmap
's config attribute is only the role name (for example, "/foo/bar/**=ROLE_FOO"), it is deleted.Example:class RoleController { def springSecurityService def delete() { def roleInstance = Role.get(params.id) try { springSecurityService.deleteRole (roleInstance flash.message = "The role was deleted" redirect action: list } catch (DataIntegrityViolationException e) { flash.message = "Unable to delete the role" redirect action: show, id: params.id } } }
clearCachedRequestmaps()
Flushes the Requestmaps cache and triggers a complete reload. If you useRequestmap
instances to secure URLs, the plugin loads and caches all Requestmap
instances as a performance optimization. This action saves database activity because the requestmaps are checked for each request. Do not allow the cache to become stale. When you create, edit or delete a Requestmap
, flush the cache. Both updateRole()
and deleteRole()
call clearCachedRequestmaps()for you. Call this method when you create a new Requestmap
or do other Requestmap
work that affects the cache.Example:class RequestmapController { def springSecurityService def save() { def requestmapInstance = new Requestmap(params) if (!requestmapInstance.save(flush: true)) { render view: 'create', model: [requestmapInstance: requestmapInstance] return } springSecurityService.clearCachedRequestmaps() flash.message = "Requestmap created" redirect action: show, id: requestmapInstance.id } }
reauthenticate()
Rebuilds an Authentication for the given username and registers it in the security context. You typically use this method after updating a user's authorities or other data that is cached in theAuthentication
or Principal
. It also removes the user from the user cache to force a refresh at next login.Example:class UserController { def springSecurityService def update() { def userInstance = User.get(params.id) params.salt = person.salt if (params.password) { params.password = springSecurityService.encodePassword( params.password, salt) def salt = … // e.g. randomly generated using a utility method params.salt = salt } userInstance.properties = params if (!userInstance.save(flush: true)) { render view: 'edit', model: [userInstance: userInstance] return } if (springSecurityService.loggedIn && springSecurityService.principal.username == userInstance.username) { springSecurityService.reauthenticate userInstance.username } flash.message = "The user was updated" redirect action: show, id: userInstance.id } }
5.3 SpringSecurityUtils
grails.plugin.springsecurity.SpringSecurityUtils
is a utility class with static methods that you can call directly without using dependency injection. It is primarily an internal class but can be called from application code.authoritiesToRoles()
Extracts role names from an array orCollection
of GrantedAuthority.getPrincipalAuthorities()
Retrieves the currently logged-in user's authorities. It is empty (but nevernull
) if the user is not logged in.parseAuthoritiesString()
Splits a comma-delimited String containing role names into aList
of GrantedAuthority.ifAllGranted()
Checks whether the current user has all specified roles (a comma-delimited String of role names). Primarily used bySecurityTagLib.ifAllGranted
.ifNotGranted()
Checks whether the current user has none of the specified roles (a comma-delimited String of role names). Primarily used bySecurityTagLib.ifNotGranted
.ifAnyGranted()
Checks whether the current user has any of the specified roles (a comma-delimited String of role names). Primarily used bySecurityTagLib.ifAnyGranted
.getSecurityConfig()
Retrieves the security part of theConfiguration
(from grails-app/conf/Config.groovy
).loadSecondaryConfig()
Used by dependent plugins to add configuration attributes.reloadSecurityConfig()
Forces a reload of the security configuration.isAjax()
Checks whether the request was triggered by an Ajax call. The standard way is to determine whetherX-Requested-With
request header is set and has the value XMLHttpRequest
. In addition, you can configure the name of the header with the grails.plugin.springsecurity.ajaxHeader
configuration attribute, but this is not recommended because all major JavaScript toolkits use the standard name. Further, you can register a closure in Config.groovy
with the name ajaxCheckClosure
that will be used to check if a request is an Ajax request. It is passed the request as its single argument, e.g.grails.plugin.springsecurity.ajaxCheckClosure = { request -> // return true or false }
&ajax=true
to your request query string.registerProvider()
Used by dependent plugins to register an AuthenticationProvider bean name.registerFilter()
Used by dependent plugins to register a filter bean name in a specified position in the filter chain.isSwitched()
Checks whether the current user switched from another user.getSwitchedUserOriginalUsername()
Gets the original user's username if the current user switched from another user.doWithAuth()
Executes a Closure with the current authentication. The one-parameter version which takes just a Closure assumes that there's an authentication in the HTTP Session and that the Closure is running in a separate thread from the web request, so theSecurityContext
and Authentication
aren't available to the standard ThreadLocal
. This is primarily of use when you explicitly launch a new thread from a controller action or service called in request scope, not from a Quartz job which isn't associated with an authentication in any thread.The two-parameter version takes a username and a Closure to authenticate as. This is will authenticate as the specified user and execute the closure with that authentication. It restores the authentication to the one that was active if it exists, or clears the context otherwise. This is similar to run-as and switch-user but is only local to the Closure.