Grails Programmer : How to secure your Grails 3 API with Spring Security REST for Grails?

For this post I will continue reusing the code I created in the How to use Spring Security Core to Secure your Grails 3 app post

In this post we are going to expose our app product announcements as JSON API and secure it with Spring Security REST for Grails plugin.

Lets create a service to encapsulate the logic.

$ grails create-service ProductAnnouncement 
| Created grails-app/services/myapp/ProductAnnouncementService.groovy
| Created src/test/groovy/myapp/ProductAnnouncementServiceSpec.groovy

with the content:


package myapp

import grails.transaction.Transactional

@Transactional
class ProductAnnoucementService {

    List<ProductAnnouncement> lastAnnouncements() {

        ProductAnnouncement.createCriteria().list {
    	    order("dateCreated", "desc")
            maxResults(1)
        }

    }
}


Lets create a controller for our API endpoints.

$ grails create-controller Api
| Created grails-app/controllers/myapp/ApiController.groovy
| Created src/test/groovy/myapp/ApiControllerSpec.groovy

Lets define a controller action to expose the last announcements as a JSON payload

package myapp

import grails.plugin.springsecurity.annotation.Secured

class ApiController {

    def productAnnoucementService

    @Secured(['ROLE_USER'])
    def announcements() {
        render(contentType: "application/json") {
            announcements {
                for(a in productAnnoucementService.lastAnnouncements()) {
                    announcement(message: a.message)
                }
            }
        }
    }
}

Install Spring Security REST for Grails

To install it we need to add a dependency to build.gradle

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:hibernate4:5.0.0"
    }
}

version "0.1"
group "myapp"

apply plugin:"eclipse"
apply plugin:"idea"
apply plugin:"war"
apply plugin:"org.grails.grails-web"
apply plugin:"org.grails.grails-gsp"
apply plugin:"asset-pipeline"

ext {
    grailsVersion = project.grailsVersion
    gradleWrapperVersion = project.gradleWrapperVersion
}

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-autoconfigure"
    compile "org.grails:grails-core"
    compile "org.springframework.boot:spring-boot-starter-actuator"
    compile "org.springframework.boot:spring-boot-starter-tomcat"
    compile "org.grails:grails-dependencies"
    compile "org.grails:grails-web-boot"
    compile "org.grails.plugins:cache"
    compile "org.grails.plugins:scaffolding"
    compile "org.grails.plugins:hibernate4"
    compile "org.hibernate:hibernate-ehcache"
    console "org.grails:grails-console"
    profile "org.grails.profiles:web:3.1.1"
    runtime "org.grails.plugins:asset-pipeline"
    runtime "com.h2database:h2"
    testCompile "org.grails:grails-plugin-testing"
    testCompile "org.grails.plugins:geb"
    testRuntime "org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.1"
    testRuntime "net.sourceforge.htmlunit:htmlunit:2.18"
    compile 'org.grails.plugins:spring-security-core:3.0.3'
    compile "org.grails.plugins:spring-security-rest:2.0.0.M2"
}

task wrapper(type: Wrapper) {
    gradleVersion = gradleWrapperVersion
}

assets {
    minifyJs = true
    minifyCss = true
}

Spring Security REST for Grails exposes an endpoint /api/login which we can access with a POST request. We should supply our credentials (username and password) as parameters. If we are successfully authenticated we will get an access_token and a refresh_token.

Untitled_Paw_Document

We can do a GET request to our http://localhost:8080/api/announcements endpoint. We need to add an HTTP header:
Header Name: Authorization
Header Value: Bearer access_token

The access_token is the one we got back from the api/login endpoint.

The above request is illustrated here:

Note were are using a JSON Web Token (JWT) which we don’t need to store on the server side. The token validation is done without a repository (database or similar) call. Thus, better for the scalability of the application.

Access token expire. However in the api/login method we got a refresh token which we can use to get a new access token. We can do POST request as shown below:

Oauth Refresh token

Do you like to read about Groovy/Grails development? Yes, then Subscribe to Groovy Calamari a weekly curated email newsletter about the Groovy ecosystem which I write 

9 thoughts on “Grails Programmer : How to secure your Grails 3 API with Spring Security REST for Grails?

  1. Farah

    What if I want to secure the “def announcements()” for 2 specific users? How can I do that?
    I have tried so far….
    @Secured([“authentication.name == ‘admin1′”, “authentication.name == ‘admin2′”])
    @Secured(“authentication.name in [‘admin1’, ‘admin2’]”)
    @Secured(“[‘admin1′,’admin2’].contains(authentication.name)”)

    None of them are working 🙁

    Reply
  2. Sergio

    Farah I believe it is a closure.

    That it is to say:

    @Secured( { [‘admin1’, ‘admin2’].contains(authentication.name) })

    Note I think your best option would be to add a role to admin1 and admin2 and secured the action based on that role.

    Reply
  3. Ibrahim H.

    Hi Sergio,
    Thank you for this great tutorial, there is just a small typo when you wrote “productAnnoucementService” but your called the service “ProductAnnouncement” (so the file would be: grails-app/services/myapp/ProductAnnouncementService.groovy), this cause the api controller to query on null object and I’ve spent sometime to discover that, but thank you anyway 🙂
    Regards

    Reply
  4. Manoj Mungara

    Thank you very much for this wonderful article. If you are issuing a rest call from an application instead of through a browser, what is the best way to accomplish the authentication?

    Reply
  5. Stefano Scarpanti

    It is very useful!
    How is possibile to simply save access login into a database? I have tried to register an URI (/api/login) interceptor to store authentication tries, but it seems /api/login is not intercepteable…

    Reply
  6. CharlesDot

    hOur company offers a wide variety of health products. Take a look at our health contributing portal in case you want to feel better. Our company provides a wide variety of non prescription products. Visit our health portal in case you want to to feel healthier with a help of health products. Our company provides a wide variety of healthcare products. Look at our health contributing site in case you want to look better. Our company provides a wide variety of non prescription products. Take a look at our health site in case you want to to feel healthier with a help of health products. Our company offers herb-based non prescription products. Visit our health contributing website in case you want to strengthen your health. Our company offers herb-based general health products. Look at our health contributing site in case you want to improve your health.
    Our company offers a wide variety of non prescription products. Visit our health website in case you want to to feel healthier with a help of generic supplements. Our company offers herb-based supplements. Look at our health contributing site in case you want to strengthen your health. Our company provides health and related products. Look at our health contributing site in case you want to feel healthier. Our company provides a wide variety of non prescription drugs. Visit our health site in case you want to feel better with a help of health products. Our company offers safe non prescription products. Visit our health contributing portal in case you want to improve your health.

    Reply

Leave a Reply to Manoj Mungara Cancel reply

Your email address will not be published. Required fields are marked *