Grails Cookbook - A collection of tutorials and examples

Grails Spring Security Core Plugin Example - Dynamic Request Maps

This example will show how to use Spring Security Core Plugin using dynamic request maps. Similar to annotations, this is also a simple way of using this plugin to secure your Grails applications.

Overview

The most popular security plugin for Grails is Spring Security Core. It is ideal to implement login and logout screens. As well as restricting access to specific screens with business rules.
In Dynamic Request Maps, role / access rules are saved in the database. It means restrictions can be changed at runtime by just database table update.

Install Spring Security Core Plugin

The first step is to install the plugin by editing grails-app/conf/BuildConfig.groovy. Find the right plugin version for your Grails project version. Add entry similar to below in the plugins section:

grails.project.dependency.resolution = {
    ...
    plugins {
        ...
        compile ":spring-security-core:1.2.7.3"
    }
}

My Grails version in this sample is 2.2.4. And the most compatible plugin version is 1.2.7.3.

Try to run the application to download the plugin packages and integrate into the project:

grails run-app

Create Domain Classes


The plugin has a script to help create the domain classes. Issue this command on the console.
grails s2-quickstart asia.grails.sample SecUser SecRole Requestmap 

The parameters are as follows: package, user class, role class, and requet map class.
Four domain classes are created (Login and Logout controllers are also created). I removed some methods for conciseness.

SecUser - represents a user. Some properties are used for the state of the user (account enabled, account expired, etc). The password is also automatically hashed before saving to the database.

package asia.grails.sample
class SecUser {
	String username
	String password
	boolean enabled
	boolean accountExpired
	boolean accountLocked
	boolean passwordExpired
	def beforeInsert() {
		encodePassword()
	}
	def beforeUpdate() {
		if (isDirty('password')) {
			encodePassword()
		}
	}
	protected void encodePassword() {
		password = springSecurityService.encodePassword(password)
	}
}

SecRole - this represents a role given to a user.

package asia.grails.sample
class SecRole {
	String authority
	...
}

SecUserSecRole - this is the middle relationship table to relate user to a role (many to many relationship)

package asia.grails.sample
class SecUserSecRole implements Serializable {
	SecUser secUser
	SecRole secRole
	...
}

Requestmap - mapping of accessible URLs by role.

package asia.grails.sample
class Requestmap {
	String url
	String configAttribute
}

Create Test Data

Here is a sample data to play with security. Requestmap data is not included yet.
import asia.grails.sample.SecRole
import asia.grails.sample.SecUser
import asia.grails.sample.SecUserSecRole
class BootStrap {
    def init = { servletContext ->
        SecUser admin = new SecUser(username:'admin', password:'secret', enabled:true).save()
        SecUser john = new SecUser(username:'john', password:'secret', enabled:true).save()
        SecUser jane = new SecUser(username:'jane', password:'secret', enabled:true).save()
        SecRole royalty = new SecRole(authority: 'ROLE_ROYALTY').save()
        SecRole common = new SecRole(authority: 'ROLE_COMMON').save()
        SecUserSecRole.create(admin, royalty)
        SecUserSecRole.create(admin, common)
        SecUserSecRole.create(john, common)
    }
    def destroy = {
    }
}
Note that in Role class, the value of authority should start with "ROLE_". We have created 3 users:
  • admin - has common and royalty role
  • john - has common role only
  • jane - can login but has no roles

Securing Controller Actions

This is a sample controller with some actions:

package sample
class ScreenController {
    def publicPage() {
        render "This is a public page"
    }
    def authenticatedPage() {
        render "This is a authenticated only page"
    }
    def commonPage() {
        render "This is a common role page"
    }
    def royalPage() {
        render "This is a royal role page"
    }
}

At this stage, you can access all the pages regardless of user used to login. Even users not logged in can access the pages.

To do the restrictions via dynamic request maps, first edit grails-app/conf/Config.groovy and add the following lines:

import grails.plugins.springsecurity.SecurityConfigType
grails.plugins.springsecurity.securityConfigType = SecurityConfigType.Requestmap

And add the following code to Bootstrap.groovy

import asia.grails.sample.Requestmap
...
class BootStrap {
    def init = { servletContext ->
        ...
        new Requestmap(url: '/screen/authenticatedPage', configAttribute: 'IS_AUTHENTICATED_FULLY').save()
        new Requestmap(url: '/screen/commonPage', configAttribute: 'ROLE_COMMON').save()
        new Requestmap(url: '/screen/royalPage', configAttribute: 'ROLE_ROYALTY').save()
        new Requestmap(url: '/**', configAttribute: 'IS_AUTHENTICATED_ANONYMOUSLY').save()
    }
    def destroy = {
    }
}

This is the behavior of each controller action after using the configuration above:
  • authenticatedPage - only logged in user can view this page. Admin, john and jane can access this page.
  • commonPage - only user with ROLE_COMMON role can access this page. Both admin and john can access this page.
  • royalPage - only user with ROLE_ROYALTY role can access this page. Only admin can access this page.
Note that the '/**' section is a catch all. And the role required is anonymous, meaning any user or visitor can access. Even for users that are not logged in. This will be the behavior of publicPage action as it is not a match with the other rules.
When a user is not logged-in and a restricted page is accessed, the user will be redirected to the login page.

When a user is logged in and an action is accessed without having the correct role, an error page is displayed:

Code

The full source code for this example can be viewed here or can be downloaded here.

Other Grails Plugins Tutorials


Tags: acegi, login, logout, Plugin, security, spring security