Securing a GWT Grails app with Shiro

A recent poster on the Grails user mailing list asked how to secure a GWT application using Shiro. The Shiro Plugin is mostly designed to work with Grails controllers and GSPs, so that’s what the current documentation targets. GWT is a different kettle of fish, so can the Shiro Plugin help here?

As it happens, I needed to handle unauthenticated GWT requests myself recently. Here’s the solution I came up with.

Imagine we have a GWT-RPC request come in to the server, but the session has timed out. First off, is the request protected? That depends on how you have your security filters configured. I currently have this:

class SecurityFilters {
    def filters = {
        ...
        gwt(controller: "gwt", action: "index") {
            before = {
                accessControl { true }
            }
        }
        ...
    }
}

Because I’m protecting the “gwt” controller, all GWT-RPC requests require an authenticated (or remembered) user. That means whenever the current user’s session times out, Shiro kicks in and asks for authentication. It does this by redirecting to the login page, but this doesn’t work within the context of a GWT-RPC request. So what can you do?

The Shiro plugin allows you to customise the behaviour when the user requires authentication. Simply implement the following method in your filters class:

class SecurityFilters {
    ...
    def onNotAuthenticated(subject, filter) {
        if (filter.request.contentType?.startsWith("text/x-gwt-rpc")) {
            filter.render(text: "", status: 401)
            return false
        }
        else {
            // Not an AJAX request, so continue with the default behaviour.
            return true
        }
    }
}

If this method is implemented, the plugin calls it whenever authentication is required. In this particular case, I return a status of 401 (Unauthorized) if the request is of type GWT-RPC. Otherwise, I trigger the default behaviour (redirection to the login page) by returning true. In other words, the return value of the method determines whether the default behaviour occurs or not.

Note The return value of onNotAuthenticated() only has an effect in version 1.1-SNAPSHOT and greater of the plugin.

That’s it for the server side. All we need to do now is handle the 401 response on the client. How you exactly do this depends on your approach to GWT requests, but the basic mechanics are the same. I use the action handler mechanism supported by the plugin, along with Google Gin, so I can simply wrap the standard action service with a version that handles 401 responses:

public class SecureActionService implements GwtActionServiceAsync {
    private final GwtActionServiceAsync actionService;

    @Inject 
    SecureActionService(final GwtActionServiceAsync actionService) {
        this.actionService = actionService;
    }   
            
    public <T extends Response> void execute(final Action<T> action, final AsyncCallback<T> callback) {
        actionService.execute(action, new AsyncCallback<T>() {
            public void onFailure(Throwable caught) {
                if (caught instanceof StatusCodeException &&
                        ((StatusCodeException) caught).getStatusCode() == 401) {
                    // Authentication/authorisation failure. Assume
                    // authentication at this stage and redirect to
                    // login page.
                    final String loginUrl = GWT.getHostPageBaseURL() + "login";
                    Window.open(loginUrl, "_self", "");
                }
                else {
                    callback.onFailure(caught);
                }
            }
        
            public void onSuccess(T result) {
                callback.onSuccess(result);
            }
        });
    }
}

Now all the client code that has the action service injected gets the custom secure service instead. This particular implementation isn’t ideal because it redirects to the standard login page for a 401, whereas it would probably be better to have a simple login popup that then resends the action on successful authentication. However, the key point is that you need to implement the onFailure() method and check for a StatusCodeException. You can then do whatever you want with a 401 response.

The solution outlined above is fairly basic, particularly as it doesn’t deal with unauthorised (as opposed to unauthenticated) access, but it should provide you with a suitable starting point to implement your own solution. You can also see how powerful the command pattern is combined with the dependency injection provided by Google Gin.

One last thing to mention: all the above is geared towards GWT-RPC. If you prefer to use JSON for the client-server communication, then you’ll need a different approach. I have no experience yet on that side of things, but you should be able to check the HTTP headers in onNotAuthenticated to determine whether a request is from GWT or not. Alternatively, simply check for the JSON content type.

5 thoughts on “Securing a GWT Grails app with Shiro

  1. foby

    Peter, thanks a lot for this. I’ve already seen myself end up in parsing the message body of the exception trying to find out what happened…

  2. Malte Hübner

    This is a great post. I will use your example to handle AJAX session timeouts in our app. Thank you Peter!

  3. Pingback: Blog bookmarks 05/27/2010 « My Diigo bookmarks

  4. Nick

    I am using Grails with Acegi, I would like to implement the same in our application. Would you pls help me how to do this.

    Many thanks in adv !

    -Nick

Leave a Reply

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