A comprehensive step by step Grails 4 tutorial on creating custom Spring Security custom user details

In this tutorial, we will show you how to implementing custom user details for Grails 4 and Spring Security web applications. As a default, the Grails 4 Spring Security generated user domain or entity has only username and role fields that can get or show in the SecurityTagLib. So, we will add additional fields to display user info or details in the SecurityTagLib.

This tutorial divided into several steps:

  • Step #1: Create a New Grails 4 Application
  • Step #2: Add Spring Security Plugin
  • Step #3: Generate User and Role Domain
  • Step #4: Create a Custom User Details
  • Step #5: Add a Secure Page
  • Step #6: Run and Test Grails 4 Spring Security Application

The following tools, frameworks, and libraries are required for this tutorial:

  1. Grails 4 (the latest stable version)
  2. Spring Security Plugin
  3. Terminal or CMD
  4. Text Editor or IDE (We are using VSCode)

Let’s get started with the main steps!

Step #1: Create a New Grails 4 Application

Before creating a new Grails 4 application, make sure that you have installed the latest Grails 4 version in your machine and can run the Grails 4 interactive console in the terminal or CMD. In the Mac terminal, we will use SDKman to install the latest Grails 4. To see the available Grails version in the SDKman, type this command.

sdk list grails

As you can see, there is some version of Grails 4 but we will install and use the stable version of Grails 4 by type this command.

sdk i grails 4.0.3
sdk u grails 4.0.3

Next, create a new Grails 4 application by type this command.

grails create-app securepage

Go to the newly created Grail 4 application. Then open the Grails project with your IDE or Text Editor.

cd securepage
code .

To working with Grails interactive console, type this command.

grails

If you see this error.

Error Error initializing classpath: Timeout of 120000 reached waiting for exclusive access to file: /Users/didin/.gradle/wrapper/dists/gradle-5.1.1-bin/90y9l8txxfw1s2o6ctiqeruwn/gradle-5.1.1-bin.zip (Use --stacktrace to see the full trace)

Just, remove .grails folder in the home directory then start again Grails interactive console.

rm -rf ~/.gradle
grails

For sanitation, start this Grails 4 application by type this command inside Grails interactive console.

run-app

Open your browser then go to “localhost:8080” and you should see this Grails page.

Grails 4 and Spring Security Custom User Details Example - Grails Home

To stop or shutdown the running Grails server, type this command.

stop-app

Step #2: Add Spring Security Plugin

To add the Grails Spring Security Plugin, open and edit “build.gradle” in the root of the project folder. Then add this line to the dependencies.

dependencies {
...
    compile 'org.grails.plugins:spring-security-core:4.0.0'
...
}

Compile this project to download and install the Spring Security dependency.

compile

Step #3: Generate User and Role Domain

Still, on Grails interactive console, type this command to generate the User and Role domain class.

s2-quickstart com.djamware.securepage User Role

That command generates the User.groovy, Role.groovy, and UserRole.groovy domain class. Also, an additional configuration file “grails-app/conf/application.groovy”. The generated User.groovy look like this.

package com.djamware.securepage

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

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

    private static final long serialVersionUID = 1

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

    Set<Role> getAuthorities() {
        (UserRole.findAllByUser(this) as List<UserRole>)*.role as Set<Role>
    }

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

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

The generated Role.groovy looks like this.

package com.djamware.securepage

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

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

    private static final long serialVersionUID = 1

    String authority

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

    static mapping = {
        cache true
    }
}

The generated UserRole.groovy looks like this.

package com.djamware.securepage

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

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

@GrailsCompileStatic
@ToString(cache=true, includeNames=true, includePackage=false)
class UserRole implements Serializable {

    private static final long serialVersionUID = 1

    User user
    Role role

    @Override
    boolean equals(other) {
        if (other instanceof UserRole) {
            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 UserRole 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) {
        UserRole.where {
            user == User.load(userId) &&
            role == Role.load(roleId)
        }
    }

    static UserRole create(User user, Role role, boolean flush = false) {
        def instance = new UserRole(user: user, role: role)
        instance.save(flush: flush)
        instance
    }

    static boolean remove(User u, Role r) {
        if (u != null && r != null) {
            UserRole.where { user == u && role == r }.deleteAll()
        }
    }

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

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

    static constraints = {
        user nullable: false
        role nullable: false, validator: { Role r, UserRole ur ->
            if (ur.user?.id) {
                if (UserRole.exists(ur.user.id, r.id)) {
                    return ['userRole.exists']
                }
            }
        }
    }

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

You don’t have to worry about login page view, it automatically handles by the Spring Security.

#grails #spring #security #web-development #developer

How to Implement Custom User Details for Grails 4 and Spring Security
3.60 GEEK