Grails

Grails Spring Security Tutorial

There are many security features in Spring Security such as authentication, authorization, instance-based control, and others. Grails developers use Spring security to secure the application.

 

1. Overview

This is an in-depth article on Grails Spring Security. Grails opensource framework is used for designing and developing full-stack applications. Spring security plugin provides spring security for Grails application. The plugin can be customized for various configuration options. Interfaces are used for extensibility.

2. Grails Spring Security

2.1 Prerequisites

Java 8 is required on the linux, windows or mac operating system. Gradle 5.4.1 version can be used for building gradle projects. Grails 3.3.10 is used for creating Grails Helloworld projects. Apache tomcat 9.0 is used as a servlet container to deploy Grails Helloworld example.

2.2 Download

You can download Java 8 from the Oracle web site . Likewise, Gradle 5.4.1 can be downloaded from this website. Grails binary distribution can be downloaded from github site. Similarly, Apache Tomcat 9.0 can be downloaded from the apache website.

2.3 Setup

2.3.1 Java Setup

You can set the environment variables for JAVA_HOME and PATH. They can be set as shown below.

Java Environment

JAVA_HOME=”/jboss/jdk1.8.0_73″
export JAVA_HOME
PATH=$JAVA_HOME/bin:$PATH
export PATH

2.3.2 Grails Setup

You can set the Grails home in the PATH as shown below:

Grails Environment

export GRAILS_HOME=/path/to/grails
export PATH="$PATH:$GRAILS_HOME/bin"

2.3.3 Gradle Setup

The environment variables for Gradle are set as below:

Gradle Environment

GRADLE_HOME="/opt/gradle/gradle-5.4.1/bin"
export GRADLE_HOME=$GRADLE_HOME/bin/
export PATH=$PATH:$GRADLE_HOME

2.4 Running Gradle

You can check the version of the gradle by using the command gradle –-version. The command for running Gradle is as below:

Gradle Version

gradle --version

The output of the executed Gradle command is shown below.

Grails Spring Security - Gradle Version
Gradle Version

2.5 Running Grails

You can check the version of the Grails by using the command “grails –v”. The command for running Grails is as below:

Grails Version

grails -v

The output of the executed Grails command is shown below.

Grails Spring Security - Grails Version
Grails Version

2.6 Hello World in Grails

Grails framework is used for full-stack application development. It is an opensource framework. It cuts down the challenges in creating web applications using Java. You can create a Grails application by using the command below:

Hello World

grails create-app HelloWorld

The output of the executed Grails command is shown below.

Grails Spring Security - Grails CreateApp
Grails CreateApp

“CreateApp” command creates the HelloWorld folder. The folder contains the Gradle build based project for Grails. Folder structure is shown below:

Grails Spring Security - HelloWorld
HelloWorld Project

Controllers are generated by using commands such as create-controller or generate-controller. You can create a controller by using the command below inside the HelloWorld folder:

Create Controller

grails create-controller Hello

A controller has action methods which are public. These methods map to a URI of a page. You can add the code to show “Greetings” inside the generated controller code. The code implementation of the HelloController Class is shown below:

Hello Controller

package helloworld
 
class HelloController {
 
    def index() { 
         
        render "Greetings"
     
    }
}

You can run the Grails app in grails console using the command below:

Run App

run-app

The snap shot of the grails console is shown below:

Grails Spring Security - Grails run-app
Grails run-app

You can access the Grails app in the browser from this URL: http://localhost:8080/ . The page rendered is shown below:

Grails Spring Security - Grails App
Grails App Running

You can select the Hello Controller and click on the link. The following page shows up:

Grails Spring Security - Grails Hello Controller
Grails Hello Controller

2.7 Testing Grails Application

Grails Framework has features for automated testing. Unit testing and functional testing can be done using the framework. You can modify the HelloWorld/src/test/groovy/helloworld/HelloControllerSpec.Groovy to test the index method. The code implemented for HelloControllerSpec is shown below:

Unit Test

package helloworld
 
import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification
 
class HelloControllerSpec extends Specification implements ControllerUnitTest {
 
    def setup() {
    }
 
    def cleanup() {
    }
 
    void "test something"() {
         
        when:
        controller.index()
         
        then:
        response.text == 'Greetings'
 
    }
}

You can run test the Grails app using the command below:

Test Grails App

grails test-app

The output of the executed grails command is shown below.

Grails Spring Security - Grails Testing
Grails Testing

2.8 Grails IDE Integration

You can configure the Groovy Eclipse plugin from the distribution site. The screen shot below shows the configuration of Groovy Eclipse plugin from Help-> Install-> New Software.

Grails Spring Security - Groovy Eclipse
Groovy Eclipse

The groovy version is set from Eclipse’s Preferences -> Groovy ->Compiler. The setting of the groovy version 2.4.16 is shown below:

Grails Spring Security - Groovy Version
Groovy Version- Eclipse

Spock plugin can be installed with eclipse from this site. The screenshot shows the spock plugin installation.

Grails Spring Security - Spocky Plugin
Spocky Plugin – Eclipse

You need to install SpringSource Tool Suite Grails Support(STS) from the distribution site. You need to also ensure that Buildship gradle Integration plugin is installed. The snapshot below shows the installed gradle version.

Grails Spring Security - Gradle plugin
Eclipse – Gradle plugin

2.9 Building with Gradle

You can import the project HelloWorld which was a Gradle project created in section 2.6. The snapshot below shows the import wizard from the Eclipse menu File-> Import.

Grails Spring Security -  Import Gradle Project
Eclipse – Import Gradle Project

After the import, Gradle Grails project can be viewed in the eclipse. The screen shot below shows the imported project.

Grails Spring Security - Eclipse Project
Gradle Grails Eclipse Project

From the Gradle tasks view, You can see all the gradle tasks. To execute the grails app, click on bootRun. The screenshot below shows the gradle tasks view.

Grails Spring Security - Gradle Tasks Eclipse View
Gradle Tasks Eclipse View

The grails app can be accessed at http://localhost:8080 when the gradle runs the Grails app on eclipse. The snapshot of the Grails app and Gradle task execution is shown below.

Grails App Running – Eclipse

The HelloController can be accessed and the page renders to show the “Greetings” message. The rendered page is shown below:

Hello Controller – Eclipse

2.10 Deploying a Grails App

War file is deployed on the typical servlet containers such as Tomcat, Jetty, etc. war command is used for generating a war file. You can deploy a Grails app on to a container which supports Java Servlet 3.0 specification. The command to create a war file is shown below:

Deploying Grails App

grails war

The output of the executed grails command is shown below.

Grails War command

The war file from build/libs can be deployed on apache tomcat. The startup script of the tomcat is executed. The screen shot shows the execution of the script and the browser rendering the page at http://localhost:8080.

Grails Deploy

The controller page is accessed by clicking on the link. The page renders as shown below in the screen shot.

Gradle Grails Controller

2.11 Grails Spring Security

2.11.1 Role Based Security

This section talks about the configuration and extension of spring security plugin for Grails apps. The configuration can be maintained in conf/application.yml. It can also be maintained in conf/application.groovy. application.yml can have the application-specific values and the defualt values can be in the DefaultSecurityConfig.groovy. The application-specific values are overridden with defaults and the settings are merged with the default configuration. The application.yml used is shown below:

application.yml

---
grails:
    profile: web
    codegen:
        defaultPackage: com.app.security
    spring:
        transactionManagement:
            proxies: false
    gorm:
        reactor:
            # Whether to translate GORM events into Reactor events
            # Disabled by default for performance reasons
            events: false
info:
    app:
        name: '@info.app.name@'
        version: '@info.app.version@'
        grailsVersion: '@info.app.grailsVersion@'
spring:
    main:
        banner-mode: "off"
    groovy:
        template:
            check-template-location: false

# Spring Actuator Endpoints are Disabled by Default
endpoints:
    enabled: false
    jmx:
        enabled: true

---
grails:
    mime:
        disable:
            accept:
                header:
                    userAgents:
                        - Gecko
                        - WebKit
                        - Presto
                        - Trident
        types:
            all: '*/*'
            atom: application/atom+xml
            css: text/css
            csv: text/csv
            form: application/x-www-form-urlencoded
            html:
              - text/html
              - application/xhtml+xml
            js: text/javascript
            json:
              - application/json
              - text/json
            multipartForm: multipart/form-data
            pdf: application/pdf
            rss: application/rss+xml
            text: text/plain
            hal:
              - application/hal+json
              - application/hal+xml
            xml:
              - text/xml
              - application/xml
    urlmapping:
        cache:
            maxsize: 1000
    controllers:
        defaultScope: singleton
    converters:
        encoding: UTF-8
    views:
        default:
            codec: html
        gsp:
            encoding: UTF-8
            htmlcodec: xml
            codecs:
                expression: html
                scriptlets: html
                taglib: none
                staticparts: none
endpoints:
    jmx:
        unique-names: true

---
hibernate:
    cache:
        queries: false
        use_second_level_cache: false
        use_query_cache: false
dataSource:
    pooled: true
    jmxExport: true
    driverClassName: org.h2.Driver
    username: sa
    password: ''

environments:
    development:
        dataSource:
            dbCreate: create-drop
            url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
    test:
        dataSource:
            dbCreate: create-drop
            url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
    production:
        dataSource:
            dbCreate: none
            url: jdbc:h2:./prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
            properties:
                jmxEnabled: true
                initialSize: 5
                maxActive: 50
                minIdle: 5
                maxIdle: 25
                maxWait: 10000
                maxAge: 600000
                timeBetweenEvictionRunsMillis: 5000
                minEvictableIdleTimeMillis: 60000
                validationQuery: SELECT 1
                validationQueryTimeout: 3
                validationInterval: 15000
                testOnBorrow: true
                testWhileIdle: true
                testOnReturn: false
                jdbcInterceptors: ConnectionState
                defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED
---
grails:
    plugin:
        springsecurity:
            apf:
                storeLastUsername: true
            useSwitchUserFilter: true
            switchUser:
                targetUrl: /secure/
            adh:
                errorPage: null # to trigger a 403
            userLookup:
                userDomainClassName: org.app.security.auth.LoginUser
                authorityJoinClassName: org.app.security.auth.LoginUserRole
            authority:
                className: org.app.security.auth.LoginRole
            controllerAnnotations:
                staticRules:
                    -
                        pattern: /
                        access:
                            - permitAll
                    -
                        pattern: /dbconsole/*
                        access:
                            - permitAll
                    -
                        pattern: /error
                        access:
                            - permitAll
                    -
                        pattern: /index
                        access:
                            - permitAll
                    -
                        pattern: /index.gsp
                        access:
                            - permitAll
                    -
                        pattern: /shutdown
                        access:
                            - permitAll
                    -
                        pattern: /assets/**
                        access:
                            - permitAll
                    -
                        pattern: /**/js/**
                        access:
                            - permitAll
                    -
                        pattern: /**/css/**
                        access:
                            - permitAll
                    -
                        pattern: /**/images/**
                        access:
                            - permitAll
                    -
                        pattern: /**/favicon.ico
                        access:
                            - permitAll
                    -
                        pattern: /login/impersonate
                        access:
                            - ROLE_SUPERVISOR

resources.groovy is configured for LoginPasswordEncoderListener . Sample resources.groovy is shown below.

resources.groovy

import com.app.security.auth.LoginPasswordEncoderListener

beans = {
    userPasswordEncoderListener(LoginPasswordEncoderListener)
}

Authentication can be done in Grails using spring security. The authentication object checks if the present user can execute a secured action. The action can be URL access, domain object securing, method level security and etc., The authentication can be pluggable using spring security. There exists an overlap between login authentication and user representation. LoginUserDataService will have an implementation of the method of findLoginUserUsername with parameter username. LoginUserDataService class code is shown below.

LoginUserService.groovy

package com.app.security.auth

import grails.gorm.services.Service
import groovy.transform.CompileStatic
import com.app.security.LoginUser

@CompileStatic
@Service(LoginUser)
interface LoginUserDataService {

    LoginUser save(String username, String password, boolean enabled)

    List findLoginUserUsername()
}

LoginUser class has properties username, password and enabled. Authorities property is implemented as a method named getAuthorities. Sample code is shown below:

LoginUser

package com.app.security

import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import grails.compiler.GrailsCompileStatic

@GrailsCompileStatic
@EqualsAndHashCode(includes='username')
@ToString(includes='username', includeNames=true, includePackage=false)
class LoginUser implements Serializable {
	private static final long serialVersionUID = 1

	String username
	String password
	boolean enabled = true
	boolean accountExpired
	boolean accountLocked
	boolean passwordExpired

	Set getAuthorities() {
		(LoginUserRole.findAllByUser(this) as List)*.role as Set
	}

	static constraints = {
		password blank: false, password: true
		username blank: false, unique: true
	}

	static mapping = {
		password column: '`password`'
	}
}

Authority class helps in presenting the user roles in the web app. URL are restricted to the users by assigning access rights. Users can have multiple roles with different access rights in the application. Default user role is a virtual role named ROLE_NO_ROLES. Typically this role does not have any secure resources for this role. A LoginRole class code sample is shown below.

LoginRole.groovy

package com.app.security


import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import grails.compiler.GrailsCompileStatic

@GrailsCompileStatic
@EqualsAndHashCode(includes='authority')
@ToString(includes='authority', includeNames=true, includePackage=false)
class LoginRole implements Serializable {

	private static final long serialVersionUID = 1
	String authority

	static constraints = {
		authority blank: false, unique: true
	}

	static mapping = {
		cache true
	}
}

A Role class will have authority, constraints, and mapping. The role names can be configured with names starting with “ROLE_”. Person and Authority have many to many relationship to each other. A LoginUserRole represents the relationship for users having more than one role. The LoginUserRole code is shown as below.

LoginUserRole.groovy

package com.app.security

import grails.gorm.DetachedCriteria
import groovy.transform.ToString

import org.codehaus.groovy.util.HashCodeHelper
import grails.compiler.GrailsCompileStatic

@SuppressWarnings(['FactoryMethodName', 'Instanceof'])
@GrailsCompileStatic
@ToString(cache=true, includeNames=true, includePackage=false)
class LoginUserRole implements Serializable {

	private static final long serialVersionUID = 1

	LoginUser user
	LoginRole role

	@Override
	boolean equals(other) {
		if (other instanceof LoginUserRole) {
			other.userId == user?.id && other.roleId == role?.id
		}
	}

	@Override
	int hashCode() {
		int hashCode = HashCodeHelper.initHash()
		if (user) {
			hashCode = HashCodeHelper.updateHash(hashCode, user.id)
		}
		if (role) {
			hashCode = HashCodeHelper.updateHash(hashCode, role.id)
		}
		hashCode
	}

	static LoginUserRole get(long userId, long roleId) {
		criteriaFor(userId, roleId).get()
	}

	static boolean exists(long userId, long roleId) {
		criteriaFor(userId, roleId).count()
	}

	private static DetachedCriteria criteriaFor(long userId, long roleId) {
		LoginUserRole.where {
			user == LoginUser.load(userId) &&
					role == Role.load(roleId)
		}
	}

	static LoginUserRole create(Login user, LoginRole role, boolean flush = false) {
		def instance = new LoginUserRole(user: user, role: role)
		instance.save(flush: flush)
		instance
	}

	static boolean remove(Login u, LoginRole r) {
		if (u != null && r != null) {
			LoginUserRole.where { user == u && role == r }.deleteAll()
		}
	}

	static int removeAll(Login u) {
		u == null ? 0 : LoginUserRole.where { user == u }.deleteAll() as int
	}

	static int removeAll(LoginRole r) {
		r == null ? 0 : LoginUserRole.where { role == r }.deleteAll() as int
	}

	static constraints = {
		role validator: { LoginRole r, LoginUserRole ur ->
			if (ur.user?.id) {
				LoginUserRole.withNewSession {
					if (LoginUserRole.exists(ur.user.id, r.id)) {
						return ['userRole.exists']
					}
				}
			}
		}
	}

	static mapping = {
		id composite: ['user', 'role']
		version false
	}
}

The command below executes the above code snippets:

Grails Run App

grails run-app

The output of the executed command is shown below.

Grails Run App

The screen shot below shows the execution of the sample code and the browser rendering the page at http://localhost:8080.

Login Security Demo

2.11.2 Security Group

A security group can consist of multiple users. The access rights can be assigned to a group. The application will have multiple groups of users having different levels of access.

2.11.3 Hierarchical Roles

Roles can be hierarchical to minimize the clutter in application request mappings. The configuration options for the hierarchical roles can be role Hierarchy and roleHierarchy entry class name. Sample hierarchy can be as shown below:

Role Hierarchy

grails.plugin.springsecurity.roleHierarchy = '''
   ROLE_SUPER_ADMIN > ROLE_FINANCE_ADMIN
   ROLE_FINANCE_ADMIN > ROLE_OPERATIONS_ADMIN
   ROLE_OPERATIONS_ADMIN > ROLE_ADMIN
'''

3. Download the Source Code

Download
You can download the full source code of this example here: Grails Spring Security Tutorial

Bhagvan Kommadi

Bhagvan Kommadi is the Founder of Architect Corner & has around 20 years’ experience in the industry, ranging from large scale enterprise development to helping incubate software product start-ups. He has done Masters in Industrial Systems Engineering at Georgia Institute of Technology (1997) and Bachelors in Aerospace Engineering from Indian Institute of Technology, Madras (1993). He is member of IFX forum,Oracle JCP and participant in Java Community Process. He founded Quantica Computacao, the first quantum computing startup in India. Markets and Markets have positioned Quantica Computacao in ‘Emerging Companies’ section of Quantum Computing quadrants. Bhagvan has engineered and developed simulators and tools in the area of quantum technology using IBM Q, Microsoft Q# and Google QScript. He has reviewed the Manning book titled : "Machine Learning with TensorFlow”. He is also the author of Packt Publishing book - "Hands-On Data Structures and Algorithms with Go".He is member of IFX forum,Oracle JCP and participant in Java Community Process. He is member of the MIT Technology Review Global Panel.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button