Friday 20 March 2015

Spring Security Implementation for a RESTful Web Service

Exposing functionality, through a web service, is no different to exposing it through a web site and therefore the security applied to the web service should be no different. On the web, the user navigates the site by accessing links to end points. Data is mutated through calls to different HTTP methods at the end point. The user opens a browser, 'GET's the media which is rendered, mutates data, and 'POST's it back to the server: Create, Read, Update, Delete, the foundation of Resource Oriented Architecture. That's no different if our client is a program making requests to those resources exposed as web service. Think of the client as 'hand rolled' browser. A browser handles the session and a web service client will also need to deal with this programmatically. The session, controlled by the server, should be created, delivered to the client and used no differently whether it is serving up requests from a browser or a web service client.

In an ideal world we could expose the exact same controllers we use for a web site as RESTful web services. Why not? it's just the same data, accessed through the same functions, on the same service. In the real world this is unlikely to happen. Controllers in our web application may need to deal with certain nuances of the view, a web page. Next time you build a web site using MVC keep in mind whether you could access them from a client other than just a browser. Could you return pure JSON objects rather than marshaling objects to a view? It's possible, I'm sure it is, I've never done it in practice but I maintain the idea of one web application being able to expose resource representations independently of the client.

On a web site, we use the cookie mechanism to allow the browser to tie independent requests together to form a session. The cookie contains a token generated upon initial authentication and is used by the server to ensure subsequent requests are coming from that authenticated principle. The servlet container generates the JSESSIONID which is included in the cookie. This ties requests together but is also persisted on the client side. Login to a web application, receive a JSESSIONID, close your browser then open it again and go back the web site. Your previous session persists, albeit only in memory, and your are automatically authenticated. In our web service we need a mechanism to do the same. We could ask the client to authenticate with the same credentials each time. If our security is the same as our web site we may ask for the username and password upon each request. There's some obvious reasons why that isn't ideal. So, this leads us to the conclusion that we need to create some kind of token upon initial authentication, return it to the client and ask them to supply that with each subsequent request. Let's just use the cookie again.

Spring Security supports persistent cookie based authentication 'out the box' with the rememberMe service. In order to implement this we need to build on the 'vanilla' implementation we would use as a basis for authentication and authorisation across our application infrastructure. You will probably already have defined this, implemented UserDetails, GrantedAuthority and the UserService interface in your system, but I will go through the basics of that to start with.

Let's start with the user, the representation of the actor within our system. In my example system I persist these detail in a database and hence these entities map directly to database tables with JPA. I also like to serialize these with simpleframework annotations.

In my model I have two implementations of GrantedAuthority. Role and Permission, the authority of each is the name. Permissions are grouped into Roles and Roles associated directly to the user. You should note in the User class, my getAuthorities implementation returns both the associated roles AND their permissions which gives flexibility when securing my resources. Here's the Role.

And the Permission
The UserDetailsService is the only other interface needed to be implemented in order to get Spring Security up and running. This is something which is not solely used by Spring Security and will probably expose functionality to applications used to manage users and associated data. The service performs this business logic, delegates CRUD operations to repository objects, (DAOs implemented with JPA), and maintains transaction control. Here's the code snippet from the service which we implement from the framework.
Now we have our implementations we can begin to build the security context which will be loaded into the web application through the usual Spring context loader. Here's the basic stanza for the authentication mechanism.
It's worth saying here that I usually like a physical split between the web and service tiers. Spring allows you to easily facilitate this with the Spring Remote package. I use the HttpInvoker to do this which exposes the service interfaces as web services. I guess you might be thinking: Why do we need an MVC controller at all when we can just expose the service functionality through Spring Remote? This is a reasonable argument. However, the service I'm exposes as a remote interface also hosts some functionality I might not want exposing. I could of course restrict access to those functions with security annotations, as we'll see later, but there's a good argument to have that functionality hosted on a private subnet and only allow access to the top tier from the outside world. The controllers in our MVC application, mapped to URIs, simply marshal requests to the remote service interfaces and we can pick and choose exactly what we expose to the outside world.

On the service tier I expose the remote interfaces
And on my MVC context I override the userService bean as an Http client to the remote service, Spring Remote does the rest, (excuse the bad pun!).
In a web application which renders a UI through a browser we'd probably just define a basic authentication processing filter, another 'out the box' component. In our web service application we will need to customize this feature and we will revist this later. Now we can look at the authorisation. In Spring Security this is facilitated by the Access Decision Manager which uses a list of voters. There are three choices out the box: Affirmation, Consensus or Unanimous based. For affirmation, only one voter in the list needs to vote positively to allow the authenticated principle access to the requested resource. Consensus; the majority. Unanimous; ... you get the picture. I only have one voter in the list so the manager type doesn't matter too much but you can see how extensible this can be. The RoleVoter bean checks the requests resource is configured with an authority granted to the authenticated principle.
All that's left to do is associate the authorities with the resources in our system. There's two ways to do this. In a web application those resources are defined by a URI and method and we use the http filter to associate the resources to authorities. We could also make the association declaratively using the @Secured annotation on a method. Below is an example of the http filter defined in the security context. The granularity of our GrantedAuthority implementation model allows us to associate permissions with those methods and package them up into Roles.
So far we've defined a model and service and made the configuration for the basic 'vanilla' Spring Security implementation. Now it's a case of adding to this to implement the cookie and make the changes required to access the end points from a client other than a browser.

We can reuse the same Authentication filter which would be used in a web site application, the UsernamePasswordAuthenticationFilter class. However, we will need to override the default username and password parameter names with whatever we want those parameter names to be. We could use the default but I don't like it as it gives away the fact we are using Spring and Java.

Utilizing the cookie is a simple case of defining a RememberMe service and plugging it into the Authentication Filter. The work is done inside the AbstractRememberMeServices, there are options here when choosing which concrete type to use. The main decision is whether to persist the cookie data or not. If so, then how to accomplish that. I favour persistence and I like to use JPA. This means defining our own token entity, dao and service. This also gives us the opportunity to take of a problem which arises from the fact we are defining our services as remote interfaces.

The option to persist the token data is facilitated in the framework with the PersistentTokenBasedRememberMeServices class. This stores a POJO, PersistentRememberMeToken, via the PersistentTokenRepository. Again there are options here on implementation of the repository, either in memory or JDBC to a defined table. The problem is that the PersistentRememberMeToken is not serializable and therefore cannot be passed across a remote interface. We can start customizing the RememberMe functionality by defining our own POJO, an entity with an association to the User model entity we defined earlier.
We can create a DAO for that model object which defines CRUD operations which could be used when managing instances outside of the relationship with the user.
We now create own own RememberMe service by sub-typing the AbstractRememberMeServices class. The two methods we are forced to implement deal with creating and reading the token model object through the repository: onLoginSuccess and processAutoLoginCookie deal with the initial authentication and cookie generation, and the subsequent authentication with the cookie accordingly.
The AbstractRememberMeServices class deals with setting the generated series and token values on the response for a successful initial authenitcation via the addCookie and setCookie functions. Ultimately the generated token ends up in a value in the Set-Cookie header with a name defined by the RememberMe service class. By default his is SPRING_SECURITY_REMEMBER_ME_COOKIE. The cookie looks a little like this -

Set-Cookie: SPRING_SECURITY_REMEMBER_ME_COOKIE=12345abcde Expires=Wed, 13 Jan 2015 22:23:01 GMT; Secure; HttpOnly

There are a couple of loose ends to tie up before we can build and deploy. For a web based application we need to specify how Spring Security should handle a request to a secured resource before authentication has taken place. The entry-point-ref attribute in the http filter specifies reference to a bean which handles this request. In our web service we don't really care about redirecting to a login page so we just send a HTTP 401 to tell the client to authenticate.

Similarly, in the authentication processing filter declaration we specify a success handler which redirect back to the originally requested resource after a successful authenitcation. Again, in our web application we don't need to bother so we just send a HTTP 200 on every successful authentication request.

Here's the success handler, it's pretty simple, I won't bother showing the entry point, that's just as simple.
And so that's it. We should now be able to run a client, such as SoapUI and call the authentication end point with the username and password credentials as parameters, see the status 200 OK returned along with the Set-Cookie value in the response header. For subsequent requests to the end point URLs secured in our http filter declaration, we will need to use the Set-Cookie header and use the token value received in the response to the initial authentication on the request. In SoapUI you can write a nice Groovy script to take the cookie value and set it as a global property.