How to use a Trait to encapsulate Spring Security Core functionality in a Grails 3 App?
grails --version
| Grails Version: 3.0.14
| Groovy Version: 2.4.5
| JVM Version: 1.8.0_45
$ grails create-app my app
| Application created at /Users/groovycalamari/Documents/myapp
Add one dependency to your build.gradle as shown below to install Spring Security Core Plugin
buildscript {
ext {
grailsVersion = project.grailsVersion
}
repositories {
mavenLocal()
maven { url "https://repo.grails.org/grails/core" }
}
dependencies {
classpath "org.grails:grails-gradle-plugin:$grailsVersion"
classpath 'com.bertramlabs.plugins:asset-pipeline-gradle:2.5.0'
classpath "org.grails.plugins:hibernate:4.3.10.5"
}
}
plugins {
id "io.spring.dependency-management" version "0.5.4.RELEASE"
}
version "0.1"
group "myapp"
apply plugin: "spring-boot"
apply plugin: "war"
apply plugin: "asset-pipeline"
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: "org.grails.grails-web"
apply plugin: "org.grails.grails-gsp"
ext {
grailsVersion = project.grailsVersion
gradleWrapperVersion = project.gradleWrapperVersion
}
assets {
minifyJs = true
minifyCss = true
}
repositories {
mavenLocal()
maven { url "https://repo.grails.org/grails/core" }
}
dependencyManagement {
imports {
mavenBom "org.grails:grails-bom:$grailsVersion"
}
applyMavenExclusions false
}
dependencies {
compile "org.springframework.boot:spring-boot-starter-logging"
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "org.springframework.boot:spring-boot-autoconfigure"
compile "org.springframework.boot:spring-boot-starter-tomcat"
compile "org.grails:grails-dependencies"
compile "org.grails:grails-web-boot"
compile "org.grails.plugins:hibernate"
compile "org.grails.plugins:cache"
compile "org.hibernate:hibernate-ehcache"
compile "org.grails.plugins:scaffolding"
runtime "org.grails.plugins:asset-pipeline"
testCompile "org.grails:grails-plugin-testing"
testCompile "org.grails.plugins:geb"
// Note: It is recommended to update to a more robust driver (Chrome, Firefox etc.)
testRuntime 'org.seleniumhq.selenium:selenium-htmlunit-driver:2.44.0'
console "org.grails:grails-console"
compile 'org.grails.plugins:spring-security-core:3.0.3'
}
task wrapper(type: Wrapper) {
gradleVersion = gradleWrapperVersion
}
Lets create the security-related domain classes:
grails s2-quickstart myapp User Role
| Creating User class 'User' and Role class 'Role' in package 'myapp'
| Rendered template Person.groovy.template to destination grails-app/domain/myapp/User.groovy
| Rendered template Authority.groovy.template to destination grails-app/domain/myapp/Role.groovy
| Rendered template PersonAuthority.groovy.template to destination grails-app/domain/myapp/UserRole.groovy
|
************************************************************
* Created security-related domain classes. Your *
* grails-app/conf/application.groovy has been updated with *
* the class names of the configured domain classes; *
* please verify that the values are correct. *
************************************************************
Add a default user to grails-app/init/BootStrap.groovy
import myapp.*
class BootStrap {
def springSecurityService
def init = { servletContext ->
def userRole = new Role('ROLE_USER').save()
def me = new User('me@sergiodelamo.com', 'groovycalamari').save()
UserRole.create me, userRole
UserRole.withSession {
it.flush()
it.clear()
}
}
def destroy = {
}
}
Create a Controller which will return the name of the logged user:
grails create-controller WhoAmI
| Created grails-app/controllers/myapp/WhoAmIController.groovy
| Created src/test/groovy/myapp/WhoAmIControllerSpec.groovy
The controller code could be something like this:
package myapp
import grails.plugin.springsecurity.annotation.Secured
class WhoAmIController {
def springSecurityService
@Secured('ROLE_USER')
def index() {
render springSecurityService.principal.username
}
}
If we start the app and hit the controller endpoint, after login, we would see the user's email:
Probably you would need to user similar Spring Security Code in almost every controller. Most of the time the domain classes will be associated to a user and you need to know who is logged in. Adding the same code over and over is a bad smell.
If you never used Traits you will probably think about a creating an abstract class and make your controllers inherit from it. Fortunately Groovy Traits offer a much better way to encapsulate this functionality. Lets create a Trait to encapsulate the Spring Security Core Functionality.
$ mkdir src/main/groovy/mapp
Lets create a file: src/main/groovy/mapp/TraitSCC.groovy
with contents:
package myapp
trait TraitSCC {
def springSecurityService
def currentUsername() {
springSecurityService.principal?.username
}
}
Now lets modify the controller to implement this Trait
package myapp
import grails.plugin.springsecurity.annotation.Secured
class WhoAmIController implements TraitSCC {
@Secured('ROLE_USER')
def index() {
render currentUsername()
}
}
If you run the app and hit the endpoint you will get the same output as before.
Ok, the Trait use is a great encapsulation of a behaviour but I still need to remember to add the implements TraitSCC code to every controller in oder to access the functionality.
Well we can do better. We can use the @Enhances annotation to make every controller implement a trait.
package myapp
import grails.artefact.Enhances
import org.grails.core.artefact.ControllerArtefactHandler
@Enhances(ControllerArtefactHandler.TYPE)
trait TraitSCC {
def springSecurityService
def currentUsername() {
springSecurityService.principal?.username
}
}
Remove the implements part from the controller:
package myapp
import grails.plugin.springsecurity.annotation.Secured
class WhoAmIController {
@Secured('ROLE_USER')
def index() {
render currentUsername()
}
}
If you run the app and hit http://localhost:8080/whoAmI you will get the email back as before.
Note: In previous versions of Grails I had to move the Traits files with the @Enhances annotation to a grails plugin and reference the plugin from the main project. It did not work if you have enhanced classes in the main project.
Tags: #gradle #spock #geb #grails #groovy #java #wordpress #woocommerce #micronaut