This tutorial describes how to securing REST API with Grails 3 (latest version 3.2.6), Spring Security Core and Spring Security Rest plugin which very good for mobile apps backend. The security backend handle by Grails Spring Security Core plugin and the REST API security handle by Grails Spring Security REST plugin.
Table of Contents:
- Create Grails 3 Application
- Add and Configure Grails 3 Spring Security Core Plugin
- Add and Configuring Grails 3 Spring Security Rest Plugin
- Test Secured REST API using Postman
This application use as a backend for our mobile apps. We will create a resource that accessible only to the authenticated user. If non-authenticated user access this resource using REST API it will response "forbidden". So, the user must log in using their credentials, if not have any credentials then the user must register first. Next, a user login using the username and password, the Grails Spring security will check or validate the credentials then return a success response with a token. The user access again the secure resource with additional headers "X-Auth-Token" with the token value.
Create Grails 3 Application
We assume that you already installed Java 8 and Grails 3. So we will jump to Grails 3 project creation. For that, just open terminal or cmd then type this command.
grails create-app MySecureRest
Go to a newly created project folder.
cd MySecureRest
To make sure everything is working smoothly during this tutorial, run Grails 3 interactive console.
grails
And then run the application.
run-app
If everything working fine and you see Grails 3 application homepage in the browser, you can continue to the next step. For now, stop the app first.
stop-app
Now, create a RESTful domain class that will be secured later. There's is a tutorial for creating REST API on this site.
create-domain-class com.mysecurerest.Product
Open and edit this generated file with your favorite text editor or IDE. Make this domain like this.
package com.mysecurerest
import grails.rest.*
@Resource(uri='/api/product')
class Product {
String prodName
String prodDesc
Double prodPrice
Date createDate = new Date()
static constraints = {
}
}
Create initial data for this Product domain class. Open and edit "grails-app/init/mysecurerest/Bootstrap.groovy" the add this lines inside init function.
package mysecurerest
import com.mysecurerest.Product
class BootStrap {
def init = { servletContext ->
def prod1 = new Product(prodName:"iPhone 7",prodDesc:"New iPhone 7 32GB",prodPrice:780).save flush:true
def prod2 = new Product(prodName:"iPhone 7 Plus",prodDesc:"New iPhone 7 Plus 128GB",prodPrice:990).save flush:true
def prod3 = new Product(prodName:"iPhone 7 SE",prodDesc:"New iPhone 7 SE 64GB",prodPrice:520).save flush:true
}
def destroy = {
}
}
Run again this application for testing the Rest API.
run-app
Open another terminal or command to test the REST API with this command.
curl -i -H "Accept: application/json" localhost:8080/api/product
Add and Configure Grails 3 Spring Security Core Plugin
Open and edit file "build.gradle" at the root of the project. Add this line in the dependencies section.
compile "org.grails.plugins:spring-security-core:3.1.1"
Compile the application in Grails 3 interactive console by type this command.
compile
Create a user, role, and relation using this command. Before doing that, we should exit from Grails 3 interactive command because this command does not activate yet in the interactive console.
exit
grails s2-quickstart com.mysecurerest User Authority
Which "s2-quickstart" is a command from Spring Security plugin and this command will generate User, Authority (Role) and UserAuthority domain class. You will see below messages if successfully generate that domain classes.
CONFIGURE SUCCESSFUL
| Creating User class 'User' and Role class 'Authority' in package 'com.mysecurerest'
| Rendered template Person.groovy.template to destination grails-app/domain/com/mysecurerest/User.groovy
| Rendered template Authority.groovy.template to destination grails-app/domain/com/mysecurerest/Authority.groovy
| Rendered template PersonAuthority.groovy.template to destination grails-app/domain/com/mysecurerest/UserAuthority.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. *
************************************************************
That command also creates configuration files with "groovy" extension in code style of Groovy. Later, in this file will put configuration related to Spring Security.
Add and Configuring Grails 3 Spring Security Rest Plugin
This time to add and configuring Grails 3 Spring Security Rest plugin. Open and edit file "build.gradle" again then add this line inside dependencies section.
compile "org.grails.plugins:spring-security-rest:2.0.0.M2"
To make this plugin works and configurable, first compile the application.
compile
Next, open and edit "grails-app/conf/application.groovy" to configure Grails Spring Security Rest plugin. First, change "staticRules" to "interceptUrlMap". Add login, logout, and product endpoints and also add "/**". Change "filterChain" with new filters. Add another configuration lines for logout endpoint, token and token storage. At the end configuration file will look like this.
// Added by the Spring Security Core plugin:
grails.plugin.springsecurity.userLookup.userDomainClassName = 'com.mysecurerest.User'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'com.mysecurerest.UserAuthority'
grails.plugin.springsecurity.authority.className = 'com.mysecurerest.Authority'
grails.plugin.springsecurity.securityConfigType = "InterceptUrlMap"
grails.plugin.springsecurity.interceptUrlMap = [
[pattern: '/', 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: '/api/login', access: ['permitAll']],
[pattern: '/api/logout', access: ['isFullyAuthenticated()']],
[pattern: '/api/product', access: ['isFullyAuthenticated()']],
[pattern: '/**', access: ['isFullyAuthenticated()']]
]
grails.plugin.springsecurity.filterChain.chainMap = [
[pattern: '/api/**', filters:'JOINED_FILTERS,-anonymousAuthenticationFilter,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter'],
[pattern: '/**', filters:'JOINED_FILTERS,-restTokenValidationFilter,-restExceptionTranslationFilter']
]
grails.plugin.springsecurity.rest.logout.endpointUrl = '/api/logout'
grails.plugin.springsecurity.rest.token.validation.useBearerToken = false
grails.plugin.springsecurity.rest.token.validation.headerName = 'X-Auth-Token'
grails.plugin.springsecurity.rest.token.storage.memcached.hosts = 'localhost:11211'
grails.plugin.springsecurity.rest.token.storage.memcached.username = ''
grails.plugin.springsecurity.rest.token.storage.memcached.password = ''
grails.plugin.springsecurity.rest.token.storage.memcached.expiration = 86400
Next, add initial user data in "grails-app/init/mysecurerest/Bootstrap.groovy".
def role1 = new Authority(authority:"ROLE_USER").save flush:true
def user1 = new User(username:"user1",password:"pwd1").save flush:true
UserAuthority.create(user1,role1)
Don't forget to change import for all domain class.
import com.mysecurerest.*
Test Secured REST API using Postman
Right now, we will use third party tools for testing REST API. We are using "Postman" the chrome extension for testing our REST API. Open "postman" chrome app/extension.
In the main text box enter endpoint URL for getting product data. On the left of text box change to "GET", then click "SEND" button.
If the response shows like below, it means product REST API is secured.
{
"timestamp": 1487220350104,
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/api/product"
}
Next, we have to test login endpoint by entering end login endpoint in the text box, on the left change to "POST". Under the text box click "body" tab and choose "raw" radio button then filled raw data like below. Then click "SEND" button.
You should see a response like this if login is successful.
{
"username": "user1",
"roles": [
"ROLE_USER"
],
"access_token": "eyJhbGciOiJIUzI1NiJ9.eyJwcmluY2lwYWwiOiJINHNJQUFBQUFBQUFBSlZTUDBcL2JRQlJcL0Rva0FJVkdvQkJJRFhRb2Jja1E3Wmlvb0lDRUxFR2tXa0lvdTlzTTlPTitadXpNa0M4b0VBd01JaWxTMVg0RnYwaTc5QUFpR3JzeGQrODRRbkxLZzNtU1wvK1wvbjM3XC9uNkhpcEd3XC90WU15Nk1uNG9zNXRJM3FlWXlOaGhtbXR1T254blVFZG9jc1p3RG16U0JoK09Wd0F1Z3hDTUxyNE5kZHNDcWdzbTR1dGJheGREVzJocmVLUjBcL011NW9sdUNoMG52K0UzZW9OUDRqVUZCNzMwb3d1QW5qTEF4Vkp1MnFrdlYyeWpWR216Qld6QUlWN3JuUlJFZzNLQzFud3ZSREIxR3lsc0FvZ0JHVzJjK0tWRGthQzY4ZXpHYVdpMm9EYlMyQW9aUVpRKzZlSldsWVo5M2RPNXVTRXV6REVaVGJxVWVIdXB0MVVOXC94K0l0S0NFck5sVFF6VFptb2lPOXdKMDc4M2VtTFgyZmZ1ODBTQUhVeTlcL0kzeFh4cUFib1wvUHYxNWt4ZnRoUlltKzZ3WHNGbzdKVGZqQmZOSGpVNzU1dXY2NWRYOXlkWUFLVHZFMHZcL3ZZK2JEWTNPZFJaV2tURE9yK25aRXRJZGw5MHprQ3krVDk3YlE4UnM4U1FYU0h5VXRSazhTQlRIRkxXc2xlbjFiR041WUMrcmJ6VVo5dzcxVm5NMTVFaDNOTTd0bCtZR2lWWjMrUHY5NTl2YVdDRmFnY3NCRWhsVDVXQUZhelpJVzZ1UHJxK21STDNlbmVZRGV6XC93WDdpTU1IUkFEQUFBPSIsInN1YiI6InVzZXIxIiwicm9sZXMiOlsiUk9MRV9VU0VSIl0sImV4cCI6MTQ4NzIyNDQxNSwiaWF0IjoxNDg3MjIwODE1fQ.w1ODU8pqw3uWEz1pz1K5he5AiCSKQgvOB4D40yCeG0g"
}
Copy the "access_token" from the response. Do the step for getting product data then click Header tab below "textbox". In the key field fill with "X-Auth-Token" and value field paste from "access_token" that previously copied.
Now, you will see a response like this.
[
{
"id": 1,
"createDate": "2017-02-16T04:20:22Z",
"prodDesc": "New iPhone 7 32GB",
"prodName": "iPhone 7",
"prodPrice": 780
},
{
"id": 2,
"createDate": "2017-02-16T04:20:22Z",
"prodDesc": "New iPhone 7 Plus 128GB",
"prodName": "iPhone 7 Plus",
"prodPrice": 990
},
{
"id": 3,
"createDate": "2017-02-16T04:20:22Z",
"prodDesc": "New iPhone 7 SE 64GB",
"prodName": "iPhone 7 SE",
"prodPrice": 520
}
]
This mean, we are creating secured Grails 3 REST API successfully. You can find the working source code from our GitHub.
That just the basic. If you need more deep learning about Groovy and Grails you can take the following cheap course:
- Mastering Grails. A Comprehensive Grails Course.
- Groovy Scripting for Developers / Testers
- Introduction to JVM Languages Clojure, Kotlin, and Groovy
Thanks.