Fork me on GitHub

Getting started

To complete this workshop, you should have installed locally:

  • JDK 7 or 8.

  • Git.

  • Gradle 2.9+.

  • Grails 3.2.6.

Also, although not mandatory, it’s highly recommended to use IntelliJ IDEA, arguably the best IDE for Groovy and Grails development. You can just use the Community Edition if you want to.

To get started, clone the workshop repo locally:

git clone https://github.com/alvarosanchez/grails-angularjs-springsecurity-workshop.git

The workshop is organised through a set of exercises that are described below, each of one on its own directory. Each exercise is self-explanatory. To highlight actions you actually need to perform, an icon is used:

1. Creating a Grails REST API (50 minutes)

Change to the ex01 folder to start working on this exercise. The final solution is provided to you in the ex01/solution folder.

The first exercise in this workshop is to create a Grails application that will contain a REST API. The application will be a todo app.

1.1. Understanding profiles (5 minutes)

Historically, the create-app and create-plugin Grails commands have always created Grails applications and plugins with the same structure and files. However, Grails 3 introduced profiles, that bring more options at creation time to start with different types of applications and plugins.

A profile defines:

  • The project’s build.gradle: dependencies, plugins installed, etc.

  • Additional commands for the CLI, such as create-domain-class or run-app.

  • Skeleton: the initial set of files and directories of the application that will be created.

  • Features: configuration subsets to bring specific behaviour. For instance, the json-views feature brings some dependencies to the build.gradle file, defines some commands, etc.

To find out what profiles are available, use the list-profiles command:

$ grails list-profiles
| Available Profiles
--------------------
* angular - A profile for creating applications using AngularJS
* rest-api - Profile for REST API applications
* base - The base profile extended by other profiles
* plugin - Profile for plugins designed to work across all profiles
* web - Profile for Web applications
* web-plugin - Profile for Plugins designed for Web applications

For more information on a particular profile use the profile-info command:

$ grails profile-info rest-api
Profile: rest-api
--------------------
Profile for REST API applications

Provided Commands:
--------------------
* create-controller - Creates a controller
* create-domain-resource - Creates a domain class that represents a resource
* create-functional-test - Creates an functional test
* create-integration-test - Creates an integration test
* create-interceptor - Creates an interceptor
* create-restful-controller - Creates a REST controller
...
list-profiles and profile-info will only work outside of a Grails application folder.

1.2. Creating a project using the rest-api profile (15 minutes)

Although we will use the angular profile in future exercises, for the purpose of this one we will start with the rest-api profile. We will also pick the hibernate and json-views features:

$ grails create-app -profile rest-api -features hibernate5,json-views todo
| Application created at /tmp/todo

The next step is to create a domain class. In this case, not only we want a domain class, but also a REST controller on top of it.

There are several ways to create a RESTful controller. The easiest way it’s to create a domain resource:

$ grails create-domain-resource todo
| Created grails-app/domain/todo/Todo.groovy
| Created src/test/groovy/todo/TodoSpec.groovy

This will create a domain class like this:

Todo.groovy
package todo

import grails.rest.*

@Resource(readOnly = false, formats = ['json', 'xml'])
class Todo {

}

Notice the @Resource annotation: it’s an AST transformation that will generate at compile time a REST controller for the domain it’s annotated to. The generated controller will have the usual CRUD methods:

  • Create: save().

  • Read: index() for all elements, and show() for a single one.

  • Update: update().

  • Delete: delete().

In addition to that, if we define the annotation parameter uri, those operations will also be mapped to the corresponding HTTP methods: POST for create(), DELETE for delete(), etc.

Add the following fields to the domain class:

String description
boolean done

This is how the domain class should finally look like:

package todo

import grails.rest.*

@Resource(uri = '/todos', readOnly = false, formats = ['json', 'xml'])
class Todo {
    String description
    boolean done
}

Before continuing, add some initial data in grails-app/init/BootStrap.groovy:

import todo.Todo

class BootStrap {

    def init = { servletContext ->
        5.times { new Todo(description: "Todo ${it+1}").save() }
    }
    def destroy = {
    }
}

Next, run the application with grails run-app. Once it’s running, you should be able to test some endpoints using curl:

  • List existing todos: curl -i 0:8080/todos

  • Create a new todo: curl -i -H "Content-Type: application/json" --data '{"description":"created from curl"}' 0:8080/todos

  • Modify the created todo: curl -i -H "Content-Type: application/json" --data '{"description":"modified from curl"}' -X PUT 0:8080/todos/6

  • Display the modified todo: curl -i 0:8080/todos/6

  • And delete it: curl -i -X DELETE 0:8080/todos/6

1.3. Writing a custom RESTful controller (15 minutes)

While a domain resource may be enough for the most simplest cases, usually you will need additional actions in your controller. So let’s get rid of the @Resource annotation and write our own controller.

Create a blank RESTful controller by running:

$ grails create-restful-controller todo.Todo
| Created grails-app/controllers/todo/TodoController.groovy

This will create a controller that extends RestfulController. grails.rest.RestfulController is the base class that provides the CRUD methods like index(), save(), etc.

Note that those methods may be overriden if you need to. RestfulController also provides some other protected methods that can as well be overriden to further customise its behaviour. Check its documentation for more information.

Now, define a mapping for this controller in grails-app/controllers/UrlMappings.groovy:

"/todos"(resources:"todo")

Run the application now, and test the CRUD operations. You should see the exact same behavior as with @Resource.

Now, let’s add a custom action that lists only the uncompleted todos:

def pending() {
    respond Todo.findAllByDone(false), view: 'index'
}

And also a URL mapping for such action:

"/pendingTodos"(controller: 'todo', action: 'pending')

If you run the application now, you should be able to test this new endpoint:

$ curl -i 0:8080/pendingTodos

You can flag one todo as completed:

curl -i -H "Content-Type: application/json" --data '{"done": true}' -X PUT 0:8080/todos/3

Then, verify that the /pendingTodos endpoint works as expected.

1.4. Customising the JSON response with JSON views (15 minutes)

The Grails Views project provides additional view technologies to the Grails framework, including JSON and Markup views. It was introduced in Grails 3.0.

JSON views are written in Groovy, end with the file extension gson and reside in the grails-app/views directory. They provide a DSL for producing output in the JSON format. A hello world example can be seen below:

grails-app/views/hello.gson
json.message {
    hello "world"
}

The above JSON view results in the output:

{"message":{ "hello":"world"}}

The json variable is an instance of StreamingJsonBuilder. See the documentation in the Groovy user guide for more information on StreamingJsonBuilder.

To get started with the JSON views for the Todo domain class, run the generate-views command:

$ grails generate-views todo.Todo
| Rendered template index.gson to destination grails-app/views/todo/index.gson
| Rendered template show.gson to destination grails-app/views/todo/show.gson
| Rendered template _domain.gson to destination grails-app/views/todo/_todo.gson
| Scaffolding completed for grails-app/domain/todo/Todo.groovy

Let’s change grails-app/views/todo/_todo.gson to put some HAL information in the response:

import todo.Todo

model {
        Todo todo
}

json {
        _links {
                self {
                        href "${g.link(resource: 'todo', absolute: true)}/${todo.id}"
                }
        }
  id todo.id
        description todo.description
        done todo.done
}

If we run the application and hit the /todos endpoint, we can see the new information:

[{
        "id": 1,
        "description": "Todo 1",
        "done": false,
        "_links": {
                "self": {
                        "href": "http://localhost:8080/todos/1"
                }
        }
}, {
  ...
}]

Fortunately, Grails' JSON views comes with support for HAL automatically:

import todo.Todo

model {
        Todo todo
}

json {
        hal.links(todo)
        id todo.id
        description todo.description
        done todo.done
}

Hit the /todos endpoint again to see the difference.

2. Creating an AngularJS front-end (50 minutes)

Change to the ex02 folder to start working on this exercise. The final solution is provided to you in the ex02/solution folder.

2.1. The angular profile (10 minutes).

The angular profile was introduced in Grails 3.1. It provides a more focused set of dependencies and commands to work with Angular JS. The angular profile inherits from the rest-api profile and therefore has all of the commands and properties that the REST profile has.

The angular profile creates applications that provides the following features:

  • Default set of commands for creating Angular artefacts.

  • Gradle plugin to manage client side dependencies.

  • Gradle plugin to execute client side unit tests.

  • Asset Pipeline plugins to ease development.

By default the Angular profile includes GSP support in order to render the index page. This is necessary because the profile is designed around asset-pipeline.

The new commands that the profile brings are:

  • create-ng-component.

  • create-ng-controller.

  • create-ng-directive.

  • create-ng-domain.

  • create-ng-module.

  • create-ng-service.

All the files generated are placed by default under grails-app/assets/javascripts. For example, the command grails create-ng-controller foo will create the file grails-app/assets/javascripts/${default package name}/controllers/fooController.js.

For every artefact created, the profile commands will also create a skeleton unit test file under src/test/javascripts

To get started with the Angular profile create an application with by specifying angular as the name of the profile:

$ grails create-app --profile angular -features hibernate,json-views todo
| Application created at /tmp/todo

Now, copy the following elements from the previous project to the created one:

  • grails-app/controllers/todo/TodoController.groovy.

  • grails-app/domain/todo/Todo.groovy.

  • grails-app/init/BootStrap.groovy.

  • grails-app/views/todo/*.

After that, re-apply the URL mappings that we previously had:

"/todos"(resources:"todo")
"/pendingTodos"(controller: 'todo', action: 'pending')

Then, run the application to ensure that the /todo and /pendingTodos are working as expected. Also, if you load the root URL, you should see something like:

localhost

2.2. Writing the AngularJS code (40 minutes)

Ultimately it’s not a goal of this workshop that you become a master in Angular JS development. A basic Angular JS knowledge is required to understand the steps performed through the exercise.

The entry point for the application, as specified by the / URL mapping, is grails-app/views/index.gsp. That file contains by default information about the application. Simply ignore it.

Find a <section> tag, and leave its body empty:

<div id="content" role="main">
    <section class="row colset-2-its">

        <!-- Our code will be here -->

    </section>
</div>

Also, remove everything under grails-app/assets/javascripts/*, as we want to write our own code.

Let’s create a domain by running the following command:

$ grails create-ng-domain todo
| Rendered template NgModuleSpec.js to destination src/test/javascripts/todo/todo/todo.todoSpec.js
| Rendered template NgModule.js to destination grails-app/assets/javascripts/todo/todo/todo.todo.js
| Rendered template NgDomainSpec.js to destination src/test/javascripts/todo/domain/TodoSpec.js
| Rendered template NgDomain.js to destination grails-app/assets/javascripts/todo/domain/Todo.js

The todo/domain/Todo.js file will export a module that will allow us to write code such as Todo.list(), todo.save(), etc. A-la-Grails! On such methods, Angular JS will transparently excute an HTTP request to the backend, by taking into account the REST mappings. For example, Todo.list() will make an HTTP GET request, todo.delete() will do a DELETE, etc.

There’s just a change we need to do on it: as our backend is at /todos and not /todo, find the string "todo/:id" and replace it by "todos/:id".

The root module, generated at grails-app/assets/javascripts/todo/todo.todo.js, is named a bit repetitive, so rename it to app.js, and leave it with the following content:

//= wrapped
//= require /angular/angular
//= require /angular/angular-resource
//= require_self
//= require_tree services
//= require_tree controllers
//= require_tree directives
//= require_tree domain
//= require_tree templates

angular.module("todo", ['ngResource']);

That will be our main file to be loaded in index.gsp. As you can see, it contains asset-pipeline instructions to include other dependencies such as Angular itself, as well as our application code.

The dependencies like angular and angular-resource are fetched by the client-dependencies Gradle plugin. You will find a clientDependencies { …​ } block in build.gradle. Those dependencies are gently handled by asset-pipeline, which takes action during the build process, to bundle and minimise them into our app.js file. In development mode, however, minimisation and bundling is not performed.

Now, let’s create a controller by running the following command:

$ grails create-ng-controller todo
| Warning Destination file src/test/javascripts/todo/todo/todo.todoSpec.js already exists, skipping...
| Rendered template NgModule.js to destination grails-app/assets/javascripts/todo/todo/todo.todo.js
| Rendered template NgControllerSpec.js to destination src/test/javascripts/todo/controllers/todoControllerSpec.js
| Rendered template NgController.js to destination grails-app/assets/javascripts/todo/controllers/todoController.js

The controller code should look like this:

//= wrapped

angular
    .module("todo")
    .controller("TodoController", TodoController);

function TodoController(Todo) {
    var vm = this;

    vm.todos = Todo.list();

    vm.newTodo = new Todo();

    vm.save = function() {
        vm.newTodo.$save({}, function() {
            vm.todos.push(angular.copy(vm.newTodo));
            vm.newTodo = new Todo();
        });
    };

    vm.delete = function(todo) {
        todo.$delete({}, function() {
            var idx = vm.todos.indexOf(todo);
            vm.todos.splice(idx, 1);
        });
    };

    vm.update = function(todo) {
        todo.$update();
    };
}

Now, let’s write the view code.

First of all, make sure our entry point app.js is loaded in index.gsp. Find the <asset:javascript> tag right before the closing </body>, and change it to point to our file:

<asset:javascript src="/todo/app.js" />

Then, find the <body> tag and change it so it refers to our controller instead of the default one that we deleted. It should look like:

<body ng-app="todo" ng-controller="TodoController as vm">

Finally, inside the <section> tag, where we left a placeholder comment, write the following code:

<div class="form">
    <input type="text" ng-model="vm.newTodo.description" />
    <button type="button" ng-click="vm.save()">add</button>
</div>

<div ng-include="'/todo/list.html'"></div>

Now, create the following file:

grails-app/assets/javascripts/todo/templates/list.tpl.html
<table>
    <tbody>
        <tr ng-repeat="t in vm.todos">
            <td>
                {{t.description}}
            </td>
            <td>
                <input type="checkbox" ng-model="t.done" ng-click="vm.update(t)" />
            </td>
            <td>
                <button type="button" ng-click="vm.delete(t)">Delete</button>
            </td>
        </tr>
    </tbody>
</table>

And you are done! Go back to your browser and load http://localhost:8080:

todo

With your browser’s developer console open, try to create new todos, mark them as complete or delete them, and you will see how Angular JS issues HTTP request to the backend we created in the first exercise.

3. Adding security with Spring Security REST (50 minutes)

For this exercise, you can just continue working in the same project as the previous exercise. The final solution is provided to you in the ex03/solution folder.

3.1. Introduction to Spring Security REST (10 minutes)

Spring Security REST is a Grails plugin that allows you to use Spring Security for a stateless, token-based, RESTful authentication.

The typical flow could be the following:

rest
Figure 1. Sequence diagram overview of the plugin behavior
  1. The client application requests and endpoint that requires authentication, so the server responds with a 401 response.

  2. The client redirects the user to the login form.

  3. The user enter credentials, and the client sends a request to the authentication endpoint. The server validates credentials, and if valid, generates, stores and sends back a token to the client.

  4. The client then stores the token internally. It will be sent on every API method request.

  5. The client sends again a request to the protected resource, passing the token as an HTTP header.

  6. The server validates the token, and if valid, executes the actual operation requested.

As per the REST definition, the client is transferring its state on every request so the server is truly stateless.

The plugin helps you to wire your existing Spring Security authentication mechanism, provides you with ready-to-use token generation strategies and comes prepackaged with JWT, Memcached, GORM, Redis and Grails Cache support for token storage.

In this exercise we will use JWT. JSON Web Token (JWT) is an IETF standard (in progress) which defines a secure way to encapsulate arbitrary data that can be sent over unsecure URL’s.

Generally speaking, JWT’s can be useful in the following use cases:

  • When generating "one click" action emails, like "delete this comment" or "add this to favorites". Instead of giving the users URL’s like /comment/delete/123, you can give them something like /comment/delete/<JWT_TOKEN>, where the JWT_TOKEN contains encapsulated information about the user and the comment, in a safe way, so authentication is not required.

  • To achieve single sign-on, by sharing a JWT across applications.

In the context of authentication and authorization, JWT will help you implement a stateless implementation, as the principal information is stored directly in the JWT.

This is how a JWT looks like:

jwt
Header

A base64-encoded JSON like:

{
    "alg": "HS256",
    "typ": "JWT"
}
Claims

A base64-encoded JSON like:

{
    "exp": 1422990129,
    "sub": "jimi",
    "roles": [
        "ROLE_ADMIN",
        "ROLE_USER"
    ],
    "iat": 1422986529
}
Signature

Depends on the algorithm specified on the header, it can be a digital signature of the base64-encoded header and claims, or an encryption of them using RSA.

3.2. Securing the REST API (20 minutes)

To install the plugin, add this to your dependencies { …​ } block in build.gradle:

compile "org.grails.plugins:spring-security-rest:2.0.0.M2"

Run grails compile to resolve dependencies and ensure everything is correct. If necessary, refresh also the project in your IDE (ie: refresh Gradle project in Intellij IDEA). Then, we need to generate our User, Role and UserRole domain classes:

$ grails s2-quickstart todo User Role
| Creating User class 'User' and Role class 'Role' in package 'todo'
| Rendered template Person.groovy.template to destination grails-app/domain/todo/User.groovy
| Rendered template Authority.groovy.template to destination grails-app/domain/todo/Role.groovy
| Rendered template PersonAuthority.groovy.template to destination grails-app/domain/todo/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.               *
************************************************************

The above command will create the file grails-app/conf/application.groovy. In order to secure our API, we need to replace the chain with pattern /** with this one:

[pattern: '/api/**',         filters: 'JOINED_FILTERS,-anonymousAuthenticationFilter,-exceptionTranslationFilter,-authenticationProcessingFilter,-securityContextPersistenceFilter,-rememberMeAuthenticationFilter']

Notice that we are appling a stateless chain to the pattern /api/**, so we need to reflect this change in a number of places:

  • In the URL mappings file.

  • In the Todo.js Angular domain.

Now, add some data in BootStrap.groovy:

Role admin = new Role("ROLE_ADMIN").save()
User user = new User("user", "pass").save()
UserRole.create(user, admin, true)

Finally, let’s restrict our API to be accessed only for ROLE_ADMIN users:

TodoController.groovy
package todo

import grails.plugin.springsecurity.annotation.Secured
import grails.rest.RestfulController

@Secured(['ROLE_ADMIN'])
class TodoController extends RestfulController {

...

}

If you run the application now and try to access the endpoint with curl, you’ll receive an error:

$ curl -i 0:8080/api/todos
HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
WWW-Authenticate: Bearer
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 01 Jun 2016 03:59:56 GMT

{"timestamp":1464753596208,"status":401,"error":"Unauthorized","message":"No message available","path":"/api/todos"}

As we saw in the introduction, we need to obtain a token first, and then pass it to the endpoint.

To get a token, make a request to the login endpoint provided by the plugin:

$ curl -i -H "Content-Type: application/json" --data '{"username":"user","password":"pass"}' 0:8080/api/login
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Cache-Control: no-store
Pragma: no-cache
Content-Type: application/json;charset=UTF-8
Content-Length: 2152
Date: Wed, 01 Jun 2016 04:04:51 GMT

{"username":"user","roles":["ROLE_ADMIN"],"token_type":"Bearer","access_token":"eyJhbGciOiJIUzI1NiJ9...","expires_in":3600,"refresh_token":"eyJhbGciOiJIUzI1NiJ9...."}

Copy the access_token part of the response, and make a request to the original endpoint, passing the token in a header:

$ curl -i -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...." 0:8080/api/todos
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Application-Context: application:development
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 01 Jun 2016 04:08:14 GMT

[{"_links":{"self":{"href":"http://0:8080/api/todos/1","hreflang":"en_US","type":"application/hal+json"}},"id":1,"description":"Todo 1","done":false},{"_links":{"self":{"href":"http://0:8080/api/todos/2","hreflang":"en_US","type":"application/hal+json"}},"id":2,"description":"Todo 2","done":false},{"_links":{"self":{"href":"http://0:8080/api/todos/3","hreflang":"en_US","type":"application/hal+json"}},"id":3,"description":"Todo 3","done":false},{"_links":{"self":{"href":"http://0:8080/api/todos/4","hreflang":"en_US","type":"application/hal+json"}},"id":4,"description":"Todo 4","done":false},{"_links":{"self":{"href":"http://0:8080/api/todos/5","hreflang":"en_US","type":"application/hal+json"}},"id":5,"description":"Todo 5","done":false}]

And voilà! Our API is now sucessfully secured with Spring Security REST.

3.3. Integrating AngularJS with Spring Security REST (20 minutes)

The final step of this workshop is to introduce a login workflow in the front-end, and put the necessary Angular JS infrastructure to make sure that the token, once obtained, is transparently sent on every request.

First of all, in index.gsp we need to display a login form if the user is not authenticated. Put the following code as the body of the <section> tag:

<div ng-switch="vm.authenticated">
    <div ng-switch-when="true">
        <div ng-include="'/todo/list.html'"></div>
    </div>
    <div ng-switch-when="false">
        <div ng-include="'/todo/login.html'"></div>
    </div>
</div>

Then, create such template:

grails-app/assets/javascripts/todo/templates/login.tpl.html
<table>
    <tbody>
    <tr>
        <td>
            Username:
        </td>
        <td>
            <input type="text" name="username" ng-model="vm.user.username" />
        </td>
    </tr>
    <tr>
        <td>
            Password:
        </td>
        <td>
            <input type="password" name="password" ng-model="vm.user.password" />
        </td>
    </tr>
    <tr>
        <td colspan="2">
            <button type="button" ng-click="vm.login()">Login</button>
        </td>
    </tr>
    </tbody>
</table>

Now, we need to put the necessary logic in the controller to handle the authentication, as well as the submission of the token once obtained:

grails-app/assets/javascripts/todo/controllers/todoController.js
//= wrapped

angular
    .module("todo")
    .factory('authInterceptor', function ($rootScope, $window) {
        return {
            request: function (config) {
                config.headers = config.headers || {};
                if ($window.sessionStorage.token) {
                    config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token;
                }
                return config;
            }
        };
    })
    .config(function ($httpProvider) {
        $httpProvider.interceptors.push('authInterceptor');
    })
    .controller("TodoController", TodoController);

function TodoController(Todo, $http, $window) {
    var vm = this;

    vm.authenticated = false;
    vm.user = {};

    vm.todos = [];

    vm.newTodo = new Todo();

    vm.login = function () {
        $http.post('/api/login', {
            username: vm.user.username,
            password: vm.user.password
        }).then(function (response) {
            vm.authenticated = true;
            $window.sessionStorage.token = response.data.access_token;
            vm.todos = Todo.list();
        });
    };

    vm.save = function() {
        vm.newTodo.$save({}, function() {
            vm.todos.push(angular.copy(vm.newTodo));
            vm.newTodo = new Todo();
        });
    };

    vm.delete = function(todo) {
        todo.$delete({}, function() {
            var idx = vm.todos.indexOf(todo);
            vm.todos.splice(idx, 1);
        });
    };

    vm.update = function(todo) {
        todo.$update();
    };
}

And that’s all!

You can run again the application, with your browser’s developer console opened, and you will see how the auhentication is performed, and once logged in, you can also see how the token is sent in the header.

If you liked this workshop share it on Twitter!

4. Acknowledgements

  • Sergio del Amo, for his fantastic help upgrading this workshop to Grails 3.2.6.